AbstractEncoder.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.transport.codec;
022 
023 import java.io.UnsupportedEncodingException;
024 
025 import java.nio.ByteBuffer;
026 
027 import java.util.Collections;
028 import java.util.HashMap;
029 import java.util.LinkedHashMap;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.UUID;
033 
034 import org.apache.qpid.transport.Range;
035 import org.apache.qpid.transport.RangeSet;
036 import org.apache.qpid.transport.Struct;
037 import org.apache.qpid.transport.Type;
038 
039 import static org.apache.qpid.transport.util.Functions.*;
040 
041 
042 /**
043  * AbstractEncoder
044  *
045  @author Rafael H. Schloming
046  */
047 
048 abstract class AbstractEncoder implements Encoder
049 {
050 
051     private static Map<Class<?>,Type> ENCODINGS = new HashMap<Class<?>,Type>();
052     static
053     {
054         ENCODINGS.put(Boolean.class, Type.BOOLEAN);
055         ENCODINGS.put(String.class, Type.STR16);
056         ENCODINGS.put(Long.class, Type.INT64);
057         ENCODINGS.put(Integer.class, Type.INT32);
058         ENCODINGS.put(Short.class, Type.INT16);
059         ENCODINGS.put(Byte.class, Type.INT8);
060         ENCODINGS.put(Map.class, Type.MAP);
061         ENCODINGS.put(List.class, Type.LIST);
062         ENCODINGS.put(Float.class, Type.FLOAT);
063         ENCODINGS.put(Double.class, Type.DOUBLE);
064         ENCODINGS.put(Character.class, Type.CHAR);
065         ENCODINGS.put(byte[].class, Type.VBIN32);
066     }
067 
068     private final Map<String,byte[]> str8cache = new LinkedHashMap<String,byte[]>()
069     {
070         @Override protected boolean removeEldestEntry(Map.Entry<String,byte[]> me)
071         {
072             return size() 4*1024;
073         }
074     };
075 
076     protected abstract void doPut(byte b);
077 
078     protected abstract void doPut(ByteBuffer src);
079 
080     protected void put(byte b)
081     {
082         doPut(b);
083     }
084 
085     protected void put(ByteBuffer src)
086     {
087         doPut(src);
088     }
089 
090     protected void put(byte[] bytes)
091     {
092         put(ByteBuffer.wrap(bytes));
093     }
094 
095     protected abstract int beginSize8();
096     protected abstract void endSize8(int pos);
097 
098     protected abstract int beginSize16();
099     protected abstract void endSize16(int pos);
100 
101     protected abstract int beginSize32();
102     protected abstract void endSize32(int pos);
103 
104     public void writeUint8(short b)
105     {
106         assert b < 0x100;
107 
108         put((byteb);
109     }
110 
111     public void writeUint16(int s)
112     {
113         assert s < 0x10000;
114 
115         put(lsb(s >>> 8));
116         put(lsb(s));
117     }
118 
119     public void writeUint32(long i)
120     {
121         assert i < 0x100000000L;
122 
123         put(lsb(i >>> 24));
124         put(lsb(i >>> 16));
125         put(lsb(i >>> 8));
126         put(lsb(i));
127     }
128 
129     public void writeSequenceNo(int i)
130     {
131         writeUint32(i);
132     }
133 
134     public void writeUint64(long l)
135     {
136         for (int i = 0; i < 8; i++)
137         {
138             put(lsb(l >> (56 - i*8)));
139         }
140     }
141 
142 
143     public void writeDatetime(long l)
144     {
145         writeUint64(l);
146     }
147 
148     private static final byte[] encode(String s, String charset)
149     {
150         try
151         {
152             return s.getBytes(charset);
153         }
154         catch (UnsupportedEncodingException e)
155         {
156             throw new RuntimeException(e);
157         }
158     }
159 
160     public void writeStr8(String s)
161     {
162         if (s == null)
163         {
164             s = "";
165         }
166 
167         byte[] bytes = str8cache.get(s);
168         if (bytes == null)
169         {
170             bytes = encode(s, "UTF-8");
171             str8cache.put(s, bytes);
172         }
173         writeUint8((shortbytes.length);
174         put(bytes);
175     }
176 
177     public void writeStr16(String s)
178     {
179         if (s == null)
180         {
181             s = "";
182         }
183 
184         byte[] bytes = encode(s, "UTF-8");
185         writeUint16(bytes.length);
186         put(bytes);
187     }
188 
189     public void writeVbin8(byte[] bytes)
190     {
191         if (bytes == null) { bytes = new byte[0]}
192         if (bytes.length > 255)
193         {
194             throw new IllegalArgumentException("array too long: " + bytes.length);
195         }
196         writeUint8((shortbytes.length);
197         put(ByteBuffer.wrap(bytes));
198     }
199 
200     public void writeVbin16(byte[] bytes)
201     {
202         if (bytes == null) { bytes = new byte[0]}
203         writeUint16(bytes.length);
204         put(ByteBuffer.wrap(bytes));
205     }
206 
207     public void writeVbin32(byte[] bytes)
208     {
209         if (bytes == null) { bytes = new byte[0]}
210         writeUint32(bytes.length);
211         put(ByteBuffer.wrap(bytes));
212     }
213 
214     public void writeSequenceSet(RangeSet ranges)
215     {
216         if (ranges == null)
217         {
218             writeUint16((short0);
219         }
220         else
221         {
222             writeUint16(ranges.size() 8);
223             for (Range range : ranges)
224             {
225                 writeSequenceNo(range.getLower());
226                 writeSequenceNo(range.getUpper());
227             }
228         }
229     }
230 
231     public void writeByteRanges(RangeSet ranges)
232     {
233         throw new Error("not implemented");
234     }
235 
236     public void writeUuid(UUID uuid)
237     {
238         long msb = 0;
239         long lsb = 0;
240         if (uuid != null)
241         {
242             msb = uuid.getMostSignificantBits();
243             lsb = uuid.getLeastSignificantBits();
244         }
245         writeUint64(msb);
246         writeUint64(lsb);
247     }
248 
249     public void writeStruct(int type, Struct s)
250     {
251         boolean empty = false;
252         if (s == null)
253         {
254             s = Struct.create(type);
255             empty = true;
256         }
257 
258         int width = s.getSizeWidth();
259         int pos = -1;
260         if (width > 0)
261         {
262             pos = beginSize(width);
263         }
264 
265         if (type > 0)
266         {
267             writeUint16(type);
268         }
269 
270         s.write(this);
271 
272         if (width > 0)
273         {
274             endSize(width, pos);
275         }
276     }
277 
278     public void writeStruct32(Struct s)
279     {
280         if (s == null)
281         {
282             writeUint32(0);
283         }
284         else
285         {
286             int pos = beginSize32();
287             writeUint16(s.getEncodedType());
288             s.write(this);
289             endSize32(pos);
290         }
291     }
292 
293     private Type encoding(Object value)
294     {
295         if (value == null)
296         {
297             return Type.VOID;
298         }
299 
300         Class klass = value.getClass();
301         Type type = resolve(klass);
302 
303         if (type == null)
304         {
305             throw new IllegalArgumentException
306                 ("unable to resolve type: " + klass + ", " + value);
307         }
308         else
309         {
310             return type;
311         }
312     }
313 
314     static final Type resolve(Class klass)
315     {
316         Type type = ENCODINGS.get(klass);
317         if (type != null)
318         {
319             return type;
320         }
321 
322         Class sup = klass.getSuperclass();
323         if (sup != null)
324         {
325             type = resolve(klass.getSuperclass());
326 
327             if (type != null)
328             {
329                 return type;
330             }
331         }
332 
333         for (Class iface : klass.getInterfaces())
334         {
335             type = resolve(iface);
336             if (type != null)
337             {
338                 return type;
339             }
340         }
341 
342         return null;
343     }
344 
345     public void writeMap(Map<String,Object> map)
346     {
347         int pos = beginSize32();
348         if (map != null)
349         {
350             writeUint32(map.size());
351             writeMapEntries(map);
352         }
353         endSize32(pos);
354     }
355 
356     protected void writeMapEntries(Map<String,Object> map)
357     {
358         for (Map.Entry<String,Object> entry : map.entrySet())
359         {
360             String key = entry.getKey();
361             Object value = entry.getValue();
362             Type type = encoding(value);
363             writeStr8(key);
364             put(type.code);
365             write(type, value);
366         }
367     }
368 
369     public void writeList(List<Object> list)
370     {
371         int pos = beginSize32();
372         if (list != null)
373         {
374             writeUint32(list.size());
375             writeListEntries(list);
376         }
377         endSize32(pos);
378     }
379 
380     protected void writeListEntries(List<Object> list)
381     {
382         for (Object value : list)
383         {
384             Type type = encoding(value);
385             put(type.code);
386             write(type, value);
387         }
388     }
389 
390     public void writeArray(List<Object> array)
391     {
392         int pos = beginSize32();
393         if (array != null)
394         {
395             writeArrayEntries(array);
396         }
397         endSize32(pos);
398     }
399 
400     protected void writeArrayEntries(List<Object> array)
401     {
402         Type type;
403 
404         if (array.isEmpty())
405         {
406             return;
407         }
408         else
409         {
410             type = encoding(array.get(0));
411         }
412 
413         put(type.code);
414 
415         writeUint32(array.size());
416 
417         for (Object value : array)
418         {
419             write(type, value);
420         }
421     }
422 
423     private void writeSize(Type t, int size)
424     {
425         if (t.fixed)
426         {
427             if (size != t.width)
428             {
429                 throw new IllegalArgumentException
430                     ("size does not match fixed width " + t.width + ": " +
431                      size);
432             }
433         }
434         else
435         {
436             writeSize(t.width, size);
437         }
438     }
439 
440     private void writeSize(int width, int size)
441     {
442         // XXX: should check lengths
443         switch (width)
444         {
445         case 1:
446             writeUint8((shortsize);
447             break;
448         case 2:
449             writeUint16(size);
450             break;
451         case 4:
452             writeUint32(size);
453             break;
454         default:
455             throw new IllegalStateException("illegal width: " + width);
456         }
457     }
458 
459     private int beginSize(int width)
460     {
461         switch (width)
462         {
463         case 1:
464             return beginSize8();
465         case 2:
466             return beginSize16();
467         case 4:
468             return beginSize32();
469         default:
470             throw new IllegalStateException("illegal width: " + width);
471         }
472     }
473 
474     private void endSize(int width, int pos)
475     {
476         switch (width)
477         {
478         case 1:
479             endSize8(pos);
480             break;
481         case 2:
482             endSize16(pos);
483             break;
484         case 4:
485             endSize32(pos);
486             break;
487         default:
488             throw new IllegalStateException("illegal width: " + width);
489         }
490     }
491 
492     private void writeBytes(Type t, byte[] bytes)
493     {
494         writeSize(t, bytes.length);
495         put(bytes);
496     }
497 
498     private <T> T coerce(Class<T> klass, Object value)
499     {
500         if (klass.isInstance(value))
501         {
502             return klass.cast(value);
503         }
504         else
505         {
506             throw new IllegalArgumentException("" + value);
507         }
508     }
509 
510     private void write(Type t, Object value)
511     {
512         switch (t)
513         {
514         case BIN8:
515         case UINT8:
516             writeUint8(coerce(Short.class, value));
517             break;
518         case INT8:
519             put(coerce(Byte.class, value));
520             break;
521         case CHAR:
522             put((byte) ((char)coerce(Character.class, value)));
523             break;
524         case BOOLEAN:
525             if (coerce(Boolean.class, value))
526             {
527                 put((byte1);
528             }
529             else
530             {
531                 put((byte0);
532             }
533             break;
534 
535         case BIN16:
536         case UINT16:
537             writeUint16(coerce(Integer.class, value));
538             break;
539 
540         case INT16:
541             writeUint16(coerce(Short.class, value));
542             break;
543 
544         case BIN32:
545         case UINT32:
546             writeUint32(coerce(Long.class, value));
547             break;
548 
549         case CHAR_UTF32:
550         case INT32:
551             writeUint32(coerce(Integer.class, value));
552             break;
553 
554         case FLOAT:
555             writeUint32(Float.floatToIntBits(coerce(Float.class, value)));
556             break;
557 
558         case BIN64:
559         case UINT64:
560         case INT64:
561         case DATETIME:
562             writeUint64(coerce(Long.class, value));
563             break;
564 
565         case DOUBLE:
566             long bits = Double.doubleToLongBits(coerce(Double.class, value));
567             writeUint64(bits);
568             break;
569 
570         case UUID:
571             writeUuid(coerce(UUID.class, value));
572             break;
573 
574         case STR8:
575             writeStr8(coerce(String.class, value));
576             break;
577 
578         case STR16:
579             writeStr16(coerce(String.class, value));
580             break;
581 
582         case STR8_LATIN:
583         case STR8_UTF16:
584         case STR16_LATIN:
585         case STR16_UTF16:
586             // XXX: need to do character conversion
587             writeBytes(t, coerce(String.class, value).getBytes());
588             break;
589 
590         case MAP:
591             writeMap((Map<String,Object>coerce(Map.class, value));
592             break;
593         case LIST:
594             writeList(coerce(List.class, value));
595             break;
596         case ARRAY:
597             writeArray(coerce(List.class, value));
598             break;
599         case STRUCT32:
600             writeStruct32(coerce(Struct.class, value));
601             break;
602 
603         case BIN40:
604         case DEC32:
605         case BIN72:
606         case DEC64:
607             // XXX: what types are we supposed to use here?
608             writeBytes(t, coerce(byte[].class, value));
609             break;
610 
611         case VOID:
612             break;
613 
614         default:
615             writeBytes(t, coerce(byte[].class, value));
616             break;
617         }
618     }
619 
620 }