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((byte) b);
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((short) bytes.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((short) bytes.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((short) 0);
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((short) size);
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((byte) 1);
528 }
529 else
530 {
531 put((byte) 0);
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 }
|