001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.transport.amqp.message;
018
019import java.util.Arrays;
020import java.util.Collection;
021
022import org.apache.qpid.proton.codec.DecoderImpl;
023import org.apache.qpid.proton.codec.EncoderImpl;
024import org.apache.qpid.proton.codec.EncodingCodes;
025import org.apache.qpid.proton.codec.PrimitiveType;
026import org.apache.qpid.proton.codec.PrimitiveTypeEncoding;
027import org.apache.qpid.proton.codec.TypeEncoding;
028import org.apache.qpid.proton.codec.WritableBuffer;
029import org.fusesource.hawtbuf.UTF8Buffer;
030
031/**
032 * AMQP Type used to allow to proton-j codec to deal with UTF8Buffer types as if
033 * they were String elements.
034 */
035public class UTF8BufferType implements PrimitiveType<UTF8Buffer> {
036
037    private final UTF8BufferEncoding largeBufferEncoding;
038    private final UTF8BufferEncoding smallBufferEncoding;
039
040    public UTF8BufferType(EncoderImpl encoder, DecoderImpl decoder) {
041        this.largeBufferEncoding = new LargeUTF8BufferEncoding(encoder, decoder);
042        this.smallBufferEncoding = new SmallUTF8BufferEncoding(encoder, decoder);
043    }
044
045    @Override
046    public Class<UTF8Buffer> getTypeClass() {
047        return UTF8Buffer.class;
048    }
049
050    @Override
051    public PrimitiveTypeEncoding<UTF8Buffer> getEncoding(UTF8Buffer value) {
052        return value.getLength() <= 255 ? smallBufferEncoding : largeBufferEncoding;
053    }
054
055    @Override
056    public PrimitiveTypeEncoding<UTF8Buffer> getCanonicalEncoding() {
057        return largeBufferEncoding;
058    }
059
060    @Override
061    public Collection<? extends PrimitiveTypeEncoding<UTF8Buffer>> getAllEncodings() {
062        return Arrays.asList(smallBufferEncoding, largeBufferEncoding);
063    }
064
065    @Override
066    public void write(UTF8Buffer value) {
067        final TypeEncoding<UTF8Buffer> encoding = getEncoding(value);
068        encoding.writeConstructor();
069        encoding.writeValue(value);
070    }
071
072    public abstract class UTF8BufferEncoding implements PrimitiveTypeEncoding<UTF8Buffer> {
073
074        private final EncoderImpl encoder;
075        private final DecoderImpl decoder;
076
077        public UTF8BufferEncoding(EncoderImpl encoder, DecoderImpl decoder) {
078            this.encoder = encoder;
079            this.decoder = decoder;
080        }
081
082        @Override
083        public int getConstructorSize() {
084            return 1;
085        }
086
087        @Override
088        public boolean isFixedSizeVal() {
089            return false;
090        }
091
092        @Override
093        public boolean encodesJavaPrimitive() {
094            return false;
095        }
096
097        /**
098         * @return the number of bytes the size portion of the encoded value requires.
099         */
100        public abstract int getSizeBytes();
101
102        @Override
103        public void writeConstructor() {
104            getEncoder().writeRaw(getEncodingCode());
105        }
106
107        @Override
108        public void writeValue(UTF8Buffer value) {
109            writeSize(value);
110            WritableBuffer buffer = getEncoder().getBuffer();
111            buffer.put(value.getData(), value.getOffset(), value.getLength());
112        }
113
114        /**
115         * Write the size of the buffer using the appropriate type (byte or int) depending
116         * on the encoding type being used.
117         *
118         * @param value
119         *      The UTF8Buffer value that is being encoded.
120         */
121        public abstract void writeSize(UTF8Buffer value);
122
123        @Override
124        public int getValueSize(UTF8Buffer value) {
125            return getSizeBytes() + value.getLength();
126        }
127
128        @Override
129        public Class<UTF8Buffer> getTypeClass() {
130            return UTF8Buffer.class;
131        }
132
133        @Override
134        public PrimitiveType<UTF8Buffer> getType() {
135            return UTF8BufferType.this;
136        }
137
138        @Override
139        public boolean encodesSuperset(TypeEncoding<UTF8Buffer> encoding) {
140            return (getType() == encoding.getType());
141        }
142
143        @Override
144        public UTF8Buffer readValue() {
145            throw new UnsupportedOperationException("No decoding to UTF8Buffer exists");
146        }
147
148        @Override
149        public void skipValue() {
150            throw new UnsupportedOperationException("No decoding to UTF8Buffer exists");
151        }
152
153        public DecoderImpl getDecoder() {
154            return decoder;
155        }
156
157        public EncoderImpl getEncoder() {
158            return encoder;
159        }
160    }
161
162    public class LargeUTF8BufferEncoding extends UTF8BufferEncoding {
163
164        public LargeUTF8BufferEncoding(EncoderImpl encoder, DecoderImpl decoder) {
165            super(encoder, decoder);
166        }
167
168        @Override
169        public byte getEncodingCode() {
170            return EncodingCodes.STR32;
171        }
172
173        @Override
174        public int getSizeBytes() {
175            return Integer.BYTES;
176        }
177
178        @Override
179        public void writeSize(UTF8Buffer value) {
180            getEncoder().getBuffer().putInt(value.getLength());
181        }
182    }
183
184    public class SmallUTF8BufferEncoding extends UTF8BufferEncoding {
185
186        public SmallUTF8BufferEncoding(EncoderImpl encoder, DecoderImpl decoder) {
187            super(encoder, decoder);
188        }
189
190        @Override
191        public byte getEncodingCode() {
192            return EncodingCodes.STR8;
193        }
194
195        @Override
196        public int getSizeBytes() {
197            return Byte.BYTES;
198        }
199
200        @Override
201        public void writeSize(UTF8Buffer value) {
202            getEncoder().getBuffer().put((byte) value.getLength());
203        }
204    }
205}