View Javadoc
1   package org.djutils.serialization;
2   
3   import java.nio.charset.Charset;
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   
9   import org.djunits.unit.AreaUnit;
10  import org.djunits.unit.DurationUnit;
11  import org.djunits.unit.EnergyUnit;
12  import org.djunits.unit.LengthUnit;
13  import org.djunits.unit.MassUnit;
14  import org.djunits.unit.MoneyPerAreaUnit;
15  import org.djunits.unit.MoneyPerDurationUnit;
16  import org.djunits.unit.MoneyPerEnergyUnit;
17  import org.djunits.unit.MoneyPerLengthUnit;
18  import org.djunits.unit.MoneyPerMassUnit;
19  import org.djunits.unit.MoneyPerVolumeUnit;
20  import org.djunits.unit.MoneyUnit;
21  import org.djunits.unit.Unit;
22  import org.djunits.unit.VolumeUnit;
23  import org.djunits.value.StorageType;
24  import org.djunits.value.ValueException;
25  import org.djunits.value.vdouble.matrix.AbstractDoubleMatrix;
26  import org.djunits.value.vdouble.matrix.DoubleMatrixUtil;
27  import org.djunits.value.vdouble.scalar.AbstractDoubleScalar;
28  import org.djunits.value.vdouble.scalar.DoubleScalarUtil;
29  import org.djunits.value.vdouble.scalar.Money;
30  import org.djunits.value.vdouble.scalar.MoneyPerArea;
31  import org.djunits.value.vdouble.scalar.MoneyPerDuration;
32  import org.djunits.value.vdouble.scalar.MoneyPerEnergy;
33  import org.djunits.value.vdouble.scalar.MoneyPerLength;
34  import org.djunits.value.vdouble.scalar.MoneyPerMass;
35  import org.djunits.value.vdouble.scalar.MoneyPerVolume;
36  import org.djunits.value.vdouble.vector.AbstractDoubleVector;
37  import org.djunits.value.vdouble.vector.DoubleVectorUtil;
38  import org.djunits.value.vfloat.matrix.AbstractFloatMatrix;
39  import org.djunits.value.vfloat.matrix.FloatMatrixUtil;
40  import org.djunits.value.vfloat.scalar.AbstractFloatScalar;
41  import org.djunits.value.vfloat.scalar.FloatMoney;
42  import org.djunits.value.vfloat.scalar.FloatMoneyPerArea;
43  import org.djunits.value.vfloat.scalar.FloatMoneyPerDuration;
44  import org.djunits.value.vfloat.scalar.FloatMoneyPerEnergy;
45  import org.djunits.value.vfloat.scalar.FloatMoneyPerLength;
46  import org.djunits.value.vfloat.scalar.FloatMoneyPerMass;
47  import org.djunits.value.vfloat.scalar.FloatMoneyPerVolume;
48  import org.djunits.value.vfloat.scalar.FloatScalarUtil;
49  import org.djunits.value.vfloat.vector.AbstractFloatVector;
50  import org.djunits.value.vfloat.vector.FloatVectorUtil;
51  import org.djutils.exceptions.Throw;
52  
53  /**
54   * Message conversions. These take into account the endianness for coding the different values. Java is by default big-endian.
55   * <p>
56   * Copyright (c) 2016-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
57   * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">OpenTrafficSim License</a>.
58   * </p>
59   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
60   * initial version Mar 1, 2017 <br>
61   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
62   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
63   */
64  public final class TypedMessage
65  {
66      /**
67       * Do not instantiate this utility class.
68       */
69      private TypedMessage()
70      {
71          // Utility class; do not instantiate.
72      }
73  
74      /** The easy converters keyed by Class. */
75      private static final Map<Class<?>, Serializer<?>> ENCODERS = new HashMap<>();
76  
77      /** All the converters that decode into primitive data when possible, keyed by prefix. */
78      static final Map<Byte, Serializer<?>> PRIMITIVE_DATA_DECODERS = new HashMap<>();
79  
80      /** All the converters that decode into arrays and matrices of Objects, keyed by prefix. */
81      private static final Map<Byte, Serializer<?>> OBJECT_DECODERS = new HashMap<>();
82  
83      /** Converter for Byte. */
84      private static final Serializer<Byte> CONVERT_BYTE = new FixedSizeObjectSerializer<Byte>(FieldTypes.BYTE_8, 1, "Byte_8")
85      {
86          @Override
87          public void serialize(final Byte object, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
88          {
89              buffer[pointer.getAndIncrement(1)] = object;
90          }
91  
92          @Override
93          public Byte deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
94          {
95              return buffer[pointer.getAndIncrement(1)];
96          }
97      };
98  
99      /** Converter for Short. */
100     private static final Serializer<Short> CONVERT_SHORT =
101             new FixedSizeObjectSerializer<Short>(FieldTypes.SHORT_16, 2, "Short_16")
102             {
103                 @Override
104                 public void serialize(final Short object, final byte[] buffer, final Pointer pointer,
105                         final EndianUtil endianUtil)
106                 {
107                     endianUtil.encodeShort(object, buffer, pointer.getAndIncrement(2));
108                 }
109 
110                 @Override
111                 public Short deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
112                 {
113                     return endianUtil.decodeShort(buffer, pointer.getAndIncrement(2));
114                 }
115             };
116 
117     /** Converter for Integer. */
118     private static final Serializer<Integer> CONVERT_INTEGER =
119             new FixedSizeObjectSerializer<Integer>(FieldTypes.INT_32, 4, "Integer_32")
120             {
121                 @Override
122                 public void serialize(final Integer object, final byte[] buffer, final Pointer pointer,
123                         final EndianUtil endianUtil)
124                 {
125                     endianUtil.encodeInt(object, buffer, pointer.getAndIncrement(4));
126                 }
127 
128                 @Override
129                 public Integer deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
130                 {
131                     return endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
132                 }
133             };
134 
135     /** Converter for Integer. */
136     private static final Serializer<Long> CONVERT_LONG = new FixedSizeObjectSerializer<Long>(FieldTypes.LONG_64, 8, "Long_64")
137     {
138         @Override
139         public void serialize(final Long object, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
140         {
141             endianUtil.encodeLong(object, buffer, pointer.getAndIncrement(8));
142         }
143 
144         @Override
145         public Long deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
146         {
147             return endianUtil.decodeLong(buffer, pointer.getAndIncrement(8));
148         }
149     };
150 
151     /** Converter for Float. */
152     private static final Serializer<Float> CONVERT_FLOAT =
153             new FixedSizeObjectSerializer<Float>(FieldTypes.FLOAT_32, 4, "Float_32")
154             {
155                 @Override
156                 public void serialize(final Float object, final byte[] buffer, final Pointer pointer,
157                         final EndianUtil endianUtil)
158                 {
159                     endianUtil.encodeFloat(object, buffer, pointer.getAndIncrement(4));
160                 }
161 
162                 @Override
163                 public Float deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
164                 {
165                     return endianUtil.decodeFloat(buffer, pointer.getAndIncrement(4));
166                 }
167             };
168 
169     /** Converter for Double. */
170     private static final Serializer<Double> CONVERT_DOUBLE =
171             new FixedSizeObjectSerializer<Double>(FieldTypes.DOUBLE_64, 8, "Double_64")
172             {
173                 @Override
174                 public void serialize(final Double object, final byte[] buffer, final Pointer pointer,
175                         final EndianUtil endianUtil)
176                 {
177                     endianUtil.encodeDouble(object, buffer, pointer.getAndIncrement(8));
178                 }
179 
180                 @Override
181                 public Double deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
182                 {
183                     return endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8));
184                 }
185             };
186 
187     /** Converter for Boolean. */
188     private static final Serializer<Boolean> CONVERT_BOOLEAN =
189             new FixedSizeObjectSerializer<Boolean>(FieldTypes.BOOLEAN_8, 1, "Boolean_8")
190             {
191                 @Override
192                 public void serialize(final Boolean object, final byte[] buffer, final Pointer pointer,
193                         final EndianUtil endianUtil)
194                 {
195                     buffer[pointer.getAndIncrement(1)] = (byte) (object ? 1 : 0);
196                 }
197 
198                 @Override
199                 public Boolean deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
200                 {
201                     return buffer[pointer.getAndIncrement(1)] == 0 ? false : true;
202                 }
203             };
204 
205     /** Converter for Character. */
206     private static final Serializer<Character> CONVERT_CHARACTER16 =
207             new FixedSizeObjectSerializer<Character>(FieldTypes.CHAR_16, 2, "Char_16")
208             {
209                 @Override
210                 public void serialize(final Character object, final byte[] buffer, final Pointer pointer,
211                         final EndianUtil endianUtil)
212                 {
213                     endianUtil.encodeChar(object, buffer, pointer.getAndIncrement(size(object)));
214                 }
215 
216                 @Override
217                 public Character deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
218                 {
219                     return endianUtil.decodeChar(buffer, pointer.getAndIncrement(2));
220                 }
221             };
222 
223     /** Converter for Character. */
224     private static final Serializer<Character> CONVERT_CHARACTER8 =
225             new FixedSizeObjectSerializer<Character>(FieldTypes.CHAR_8, 1, "Char_8")
226             {
227                 @Override
228                 public void serialize(final Character object, final byte[] buffer, final Pointer pointer,
229                         final EndianUtil endianUtil)
230                 {
231                     buffer[pointer.getAndIncrement(size(object))] = (byte) (object & 0xFF);
232                 }
233 
234                 @Override
235                 public Character deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
236                 {
237                     return Character.valueOf((char) buffer[pointer.getAndIncrement(1)]);
238                 }
239             };
240 
241     /** Converter for String. */
242     private static final Serializer<String> CONVERT_STRING16 = new ObjectSerializer<String>(FieldTypes.STRING_16, "String_16")
243     {
244         @Override
245         public int size(final String object)
246         {
247             return 4 + object.getBytes(UTF16).length;
248         }
249 
250         @Override
251         public void serialize(final String string, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
252         {
253             // System.out.println("Encoding string \"" + string + "\"");
254             char[] chars = new char[string.length()];
255             string.getChars(0, chars.length, chars, 0);
256             endianUtil.encodeInt(chars.length, buffer, pointer.getAndIncrement(4));
257             // int originalPos = pointer.get();
258             for (char c : chars)
259             {
260                 endianUtil.encodeChar(c, buffer, pointer.getAndIncrement(2));
261             }
262             // System.out.println("encoded string starts at " + originalPos);
263             // System.out.print(HexDumper.hexDumper(buffer));
264         }
265 
266         @Override
267         public String deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
268         {
269             // System.out.println("Input bytes");
270             // System.out.print(HexDumper.hexDumper(buffer));
271             String s = endianUtil.decodeUTF16String(buffer, pointer.get());
272             pointer.getAndIncrement(4 + s.length() * 2);
273             return s;
274         }
275     };
276 
277     /** Converter for String. */
278     private static final Serializer<String> CONVERT_STRING8 = new ObjectSerializer<String>(FieldTypes.STRING_8, "String_8")
279     {
280         @Override
281         public int size(final String string)
282         {
283             return 4 + string.getBytes(UTF8).length;
284         }
285 
286         @Override
287         public void serialize(final String string, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
288         {
289             byte[] s = string.getBytes(UTF8);
290             endianUtil.encodeInt(s.length, buffer, pointer.getAndIncrement(4));
291             for (byte b : s)
292             {
293                 buffer[pointer.getAndIncrement(1)] = b;
294             }
295         }
296 
297         @Override
298         public String deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
299                 throws SerializationException
300         {
301             int bytesUsed = endianUtil.decodeInt(buffer, pointer.get());
302             String s = endianUtil.decodeUTF8String(buffer, pointer.get());
303             pointer.getAndIncrement(4 + bytesUsed);
304             return s;
305         }
306     };
307 
308     /** Converter for byte array. */
309     private static final Serializer<byte[]> CONVERT_BT_ARRAY =
310             new BasicPrimitiveArrayOrMatrixSerializer<byte[]>(FieldTypes.BYTE_8_ARRAY, 1, "byte_8_array", 1)
311             {
312                 @Override
313                 public int size(final byte[] array)
314                 {
315                     return 4 + getElementSize() * array.length;
316                 }
317 
318                 @Override
319                 public void serialize(final byte[] array, final byte[] buffer, final Pointer pointer,
320                         final EndianUtil endianUtil) throws SerializationException
321                 {
322                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
323                     for (int i = 0; i < array.length; i++)
324                     {
325                         buffer[pointer.getAndIncrement(getElementSize())] = array[i];
326                     }
327                 }
328 
329                 @Override
330                 public byte[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
331                         throws SerializationException
332                 {
333                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
334                     byte[] result = new byte[size];
335                     for (int i = 0; i < size; i++)
336                     {
337                         result[i] = buffer[pointer.getAndIncrement(getElementSize())];
338                     }
339                     return result;
340                 }
341             };
342 
343     /** Converter for Byte array. */
344     private static final Serializer<Byte[]> CONVERT_BYTE_ARRAY =
345             new ObjectArraySerializer<Byte>(FieldTypes.BYTE_8_ARRAY, 1, Byte.valueOf((byte) 0), "Byte_8_array")
346             {
347                 @Override
348                 public void serializeElement(final Byte object, final byte[] buffer, final int offset,
349                         final EndianUtil endianUtil)
350                 {
351                     buffer[offset] = object;
352                 }
353 
354                 @Override
355                 public Byte deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
356                 {
357                     return buffer[offset];
358                 }
359             };
360 
361     /** Converter for short array. */
362     private static final Serializer<short[]> CONVERT_SHRT_ARRAY =
363             new BasicPrimitiveArrayOrMatrixSerializer<short[]>(FieldTypes.SHORT_16_ARRAY, 2, "short_16_array", 1)
364             {
365                 @Override
366                 public int size(final short[] array)
367                 {
368                     return 4 + getElementSize() * array.length;
369                 }
370 
371                 @Override
372                 public void serialize(final short[] array, final byte[] buffer, final Pointer pointer,
373                         final EndianUtil endianUtil) throws SerializationException
374                 {
375                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
376                     for (int i = 0; i < array.length; i++)
377                     {
378                         endianUtil.encodeShort(array[i], buffer, pointer.getAndIncrement(getElementSize()));
379                     }
380                 }
381 
382                 @Override
383                 public short[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
384                         throws SerializationException
385                 {
386                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
387                     short[] result = new short[size];
388                     for (int i = 0; i < size; i++)
389                     {
390                         result[i] = endianUtil.decodeShort(buffer, pointer.getAndIncrement(getElementSize()));
391                     }
392                     return result;
393                 }
394             };
395 
396     /** Converter for Short array. */
397     private static final Serializer<Short[]> CONVERT_SHORT_ARRAY =
398             new ObjectArraySerializer<Short>(FieldTypes.SHORT_16_ARRAY, 2, Short.valueOf((short) 0), "Short_16_array")
399             {
400                 @Override
401                 public void serializeElement(final Short object, final byte[] buffer, final int offset,
402                         final EndianUtil endianUtil)
403                 {
404                     endianUtil.encodeShort(object, buffer, offset);
405                 }
406 
407                 @Override
408                 public Short deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
409                 {
410                     return endianUtil.decodeShort(buffer, offset);
411                 }
412             };
413 
414     /** Converter for int array. */
415     private static final Serializer<int[]> CONVERT_INT_ARRAY =
416             new BasicPrimitiveArrayOrMatrixSerializer<int[]>(FieldTypes.INT_32_ARRAY, 4, "int_32_array", 1)
417             {
418                 @Override
419                 public int size(final int[] array)
420                 {
421                     return 4 + getElementSize() * array.length;
422                 }
423 
424                 @Override
425                 public void serialize(final int[] array, final byte[] buffer, final Pointer pointer,
426                         final EndianUtil endianUtil) throws SerializationException
427                 {
428                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
429                     for (int i = 0; i < array.length; i++)
430                     {
431                         endianUtil.encodeInt(array[i], buffer, pointer.getAndIncrement(getElementSize()));
432                     }
433                 }
434 
435                 @Override
436                 public int[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
437                         throws SerializationException
438                 {
439                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
440                     int[] result = new int[size];
441                     for (int i = 0; i < size; i++)
442                     {
443                         result[i] = endianUtil.decodeInt(buffer, pointer.getAndIncrement(getElementSize()));
444                     }
445                     return result;
446                 }
447             };
448 
449     /** Converter for Integer array. */
450     private static final Serializer<Integer[]> CONVERT_INTEGER_ARRAY =
451             new ObjectArraySerializer<Integer>(FieldTypes.INT_32_ARRAY, 4, Integer.valueOf(0), "Integer_32_array")
452             {
453                 @Override
454                 public void serializeElement(final Integer object, final byte[] buffer, final int offset,
455                         final EndianUtil endianUtil)
456                 {
457                     endianUtil.encodeInt(object, buffer, offset);
458                 }
459 
460                 @Override
461                 public Integer deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
462                 {
463                     return endianUtil.decodeInt(buffer, offset);
464                 }
465             };
466 
467     /** Converter for long array. */
468     private static final Serializer<long[]> CONVERT_LNG_ARRAY =
469             new BasicPrimitiveArrayOrMatrixSerializer<long[]>(FieldTypes.LONG_64_ARRAY, 8, "long_64_array", 1)
470             {
471                 @Override
472                 public int size(final long[] array)
473                 {
474                     return 4 + getElementSize() * array.length;
475                 }
476 
477                 @Override
478                 public void serialize(final long[] array, final byte[] buffer, final Pointer pointer,
479                         final EndianUtil endianUtil) throws SerializationException
480                 {
481                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
482                     for (int i = 0; i < array.length; i++)
483                     {
484                         endianUtil.encodeLong(array[i], buffer, pointer.getAndIncrement(getElementSize()));
485                     }
486                 }
487 
488                 @Override
489                 public long[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
490                         throws SerializationException
491                 {
492                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
493                     long[] result = new long[size];
494                     for (int i = 0; i < size; i++)
495                     {
496                         result[i] = endianUtil.decodeLong(buffer, pointer.getAndIncrement(getElementSize()));
497                     }
498                     return result;
499                 }
500             };
501 
502     /** Converter for Long array. */
503     private static final Serializer<Long[]> CONVERT_LONG_ARRAY =
504             new ObjectArraySerializer<Long>(FieldTypes.LONG_64_ARRAY, 8, Long.valueOf(0), "Long_64_array")
505             {
506                 @Override
507                 public void serializeElement(final Long object, final byte[] buffer, final int offset,
508                         final EndianUtil endianUtil)
509                 {
510                     endianUtil.encodeLong(object, buffer, offset);
511                 }
512 
513                 @Override
514                 public Long deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
515                 {
516                     return endianUtil.decodeLong(buffer, offset);
517                 }
518             };
519 
520     /** Converter for float array. */
521     private static final Serializer<float[]> CONVERT_FLT_ARRAY =
522             new BasicPrimitiveArrayOrMatrixSerializer<float[]>(FieldTypes.FLOAT_32_ARRAY, 4, "float_32_array", 1)
523             {
524                 @Override
525                 public int size(final float[] array)
526                 {
527                     return 4 + getElementSize() * array.length;
528                 }
529 
530                 @Override
531                 public void serialize(final float[] array, final byte[] buffer, final Pointer pointer,
532                         final EndianUtil endianUtil) throws SerializationException
533                 {
534                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
535                     for (int i = 0; i < array.length; i++)
536                     {
537                         endianUtil.encodeFloat(array[i], buffer, pointer.getAndIncrement(getElementSize()));
538                     }
539                 }
540 
541                 @Override
542                 public float[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
543                         throws SerializationException
544                 {
545                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
546                     float[] result = new float[size];
547                     for (int i = 0; i < size; i++)
548                     {
549                         result[i] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(getElementSize()));
550                     }
551                     return result;
552                 }
553             };
554 
555     /** Converter for Float array. */
556     private static final Serializer<Float[]> CONVERT_FLOAT_ARRAY =
557             new ObjectArraySerializer<Float>(FieldTypes.FLOAT_32_ARRAY, 4, new Float(0), "Float_32_array")
558             {
559                 @Override
560                 public void serializeElement(final Float object, final byte[] buffer, final int offset,
561                         final EndianUtil endianUtil)
562                 {
563                     endianUtil.encodeFloat(object, buffer, offset);
564                 }
565 
566                 @Override
567                 public Float deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
568                 {
569                     return endianUtil.decodeFloat(buffer, offset);
570                 }
571             };
572 
573     /** Converter for double array. */
574     private static final Serializer<double[]> CONVERT_DBL_ARRAY =
575             new BasicPrimitiveArrayOrMatrixSerializer<double[]>(FieldTypes.DOUBLE_64_ARRAY, 8, "double_64_array", 1)
576             {
577                 @Override
578                 public int size(final double[] array)
579                 {
580                     return 4 + getElementSize() * array.length;
581                 }
582 
583                 @Override
584                 public void serialize(final double[] array, final byte[] buffer, final Pointer pointer,
585                         final EndianUtil endianUtil) throws SerializationException
586                 {
587                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
588                     for (int i = 0; i < array.length; i++)
589                     {
590                         endianUtil.encodeDouble(array[i], buffer, pointer.getAndIncrement(getElementSize()));
591                     }
592                 }
593 
594                 @Override
595                 public double[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
596                         throws SerializationException
597                 {
598                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
599                     double[] result = new double[size];
600                     for (int i = 0; i < size; i++)
601                     {
602                         result[i] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(getElementSize()));
603                     }
604                     return result;
605                 }
606             };
607 
608     /** Converter for Double array. */
609     private static final Serializer<Double[]> CONVERT_DOUBLE_ARRAY =
610             new ObjectArraySerializer<Double>(FieldTypes.DOUBLE_64_ARRAY, 8, new Double(0), "Double_64_array")
611             {
612                 @Override
613                 public void serializeElement(final Double object, final byte[] buffer, final int offset,
614                         final EndianUtil endianUtil)
615                 {
616                     endianUtil.encodeDouble(object, buffer, offset);
617                 }
618 
619                 @Override
620                 public Double deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
621                 {
622                     return endianUtil.decodeDouble(buffer, offset);
623                 }
624             };
625 
626     /** Converter for boolean array. */
627     private static final Serializer<boolean[]> CONVERT_BOOL_ARRAY =
628             new BasicPrimitiveArrayOrMatrixSerializer<boolean[]>(FieldTypes.BOOLEAN_8_ARRAY, 1, "bool_8_array", 1)
629             {
630                 @Override
631                 public int size(final boolean[] array)
632                 {
633                     return 4 + getElementSize() * array.length;
634                 }
635 
636                 @Override
637                 public void serialize(final boolean[] array, final byte[] buffer, final Pointer pointer,
638                         final EndianUtil endianUtil) throws SerializationException
639                 {
640                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
641                     for (int i = 0; i < array.length; i++)
642                     {
643                         buffer[pointer.getAndIncrement(getElementSize())] = (byte) (array[i] ? 1 : 0);
644                     }
645                 }
646 
647                 @Override
648                 public boolean[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
649                         throws SerializationException
650                 {
651                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
652                     boolean[] result = new boolean[size];
653                     for (int i = 0; i < size; i++)
654                     {
655                         result[i] = buffer[pointer.getAndIncrement(getElementSize())] == 0 ? false : true;
656                     }
657                     return result;
658                 }
659             };
660 
661     /** Converter for Boolean array. */
662     private static final Serializer<Boolean[]> CONVERT_BOOLEAN_ARRAY =
663             new ObjectArraySerializer<Boolean>(FieldTypes.BOOLEAN_8_ARRAY, 1, Boolean.FALSE, "Boolean_8_array")
664             {
665                 @Override
666                 public void serializeElement(final Boolean object, final byte[] buffer, final int offset,
667                         final EndianUtil endianUtil)
668                 {
669                     buffer[offset] = (byte) (object ? 1 : 0);
670                 }
671 
672                 @Override
673                 public Boolean deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
674                 {
675                     return buffer[offset] == 0 ? false : true;
676                 }
677             };
678 
679     /** Converter for byte matrix. */
680     private static final Serializer<byte[][]> CONVERT_BT_MATRIX =
681             new BasicPrimitiveArrayOrMatrixSerializer<byte[][]>(FieldTypes.BYTE_8_MATRIX, 1, "byte_8_matrix", 2)
682             {
683                 @Override
684                 public int size(final byte[][] matrix)
685                 {
686                     return 8 + getElementSize() * matrix.length * matrix[0].length;
687                 }
688 
689                 @Override
690                 public void serialize(final byte[][] matrix, final byte[] buffer, final Pointer pointer,
691                         final EndianUtil endianUtil) throws SerializationException
692                 {
693                     int height = matrix.length;
694                     int width = matrix[0].length;
695                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
696                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
697                     for (int i = 0; i < height; i++)
698                     {
699                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
700                         for (int j = 0; j < width; j++)
701                         {
702                             buffer[pointer.getAndIncrement(getElementSize())] = matrix[i][j];
703                         }
704                     }
705                 }
706 
707                 @Override
708                 public byte[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
709                         throws SerializationException
710                 {
711                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
712                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
713                     byte[][] result = new byte[height][width];
714                     for (int i = 0; i < height; i++)
715                     {
716                         for (int j = 0; j < width; j++)
717                         {
718                             result[i][j] = buffer[pointer.getAndIncrement(getElementSize())];
719                         }
720                     }
721                     return result;
722                 }
723             };
724 
725     /** Converter for Byte matrix. */
726     private static final Serializer<Byte[][]> CONVERT_BYTE_MATRIX =
727             new ObjectMatrixSerializer<Byte>(FieldTypes.BYTE_8_MATRIX, 1, Byte.class, "Byte_8_matrix")
728             {
729                 @Override
730                 public void serializeElement(final Byte object, final byte[] buffer, final int offset,
731                         final EndianUtil endianUtil)
732                 {
733                     buffer[offset] = object;
734                 }
735 
736                 @Override
737                 public Byte deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
738                 {
739                     return buffer[offset];
740                 }
741             };
742 
743     /** Converter for short matrix. */
744     private static final Serializer<short[][]> CONVERT_SHRT_MATRIX =
745             new BasicPrimitiveArrayOrMatrixSerializer<short[][]>(FieldTypes.SHORT_16_MATRIX, 2, "short_16_matrix", 2)
746             {
747                 @Override
748                 public int size(final short[][] matrix)
749                 {
750                     return 8 + getElementSize() * matrix.length * matrix[0].length;
751                 }
752 
753                 @Override
754                 public void serialize(final short[][] matrix, final byte[] buffer, final Pointer pointer,
755                         final EndianUtil endianUtil) throws SerializationException
756                 {
757                     int height = matrix.length;
758                     int width = matrix[0].length;
759                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
760                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
761                     for (int i = 0; i < height; i++)
762                     {
763                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
764                         for (int j = 0; j < width; j++)
765                         {
766                             endianUtil.encodeShort(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
767                         }
768                     }
769                 }
770 
771                 @Override
772                 public short[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
773                         throws SerializationException
774                 {
775                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
776                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
777                     short[][] result = new short[height][width];
778                     for (int i = 0; i < height; i++)
779                     {
780                         for (int j = 0; j < width; j++)
781                         {
782                             result[i][j] = endianUtil.decodeShort(buffer, pointer.getAndIncrement(getElementSize()));
783                         }
784                     }
785                     return result;
786                 }
787             };
788 
789     /** Converter for Short matrix. */
790     private static final Serializer<Short[][]> CONVERT_SHORT_MATRIX =
791             new ObjectMatrixSerializer<Short>(FieldTypes.SHORT_16_MATRIX, 2, Short.class, "Short_16_matrix")
792             {
793                 @Override
794                 public void serializeElement(final Short object, final byte[] buffer, final int offset,
795                         final EndianUtil endianUtil)
796                 {
797                     endianUtil.encodeShort(object, buffer, offset);
798                 }
799 
800                 @Override
801                 public Short deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
802                 {
803                     return endianUtil.decodeShort(buffer, offset);
804                 }
805             };
806 
807     /** Converter for int matrix. */
808     private static final Serializer<int[][]> CONVERT_INT_MATRIX =
809             new BasicPrimitiveArrayOrMatrixSerializer<int[][]>(FieldTypes.INT_32_MATRIX, 4, "int_32_matrix", 2)
810             {
811                 @Override
812                 public int size(final int[][] matrix)
813                 {
814                     return 8 + getElementSize() * matrix.length * matrix[0].length;
815                 }
816 
817                 @Override
818                 public void serialize(final int[][] matrix, final byte[] buffer, final Pointer pointer,
819                         final EndianUtil endianUtil) throws SerializationException
820                 {
821                     int height = matrix.length;
822                     int width = matrix[0].length;
823                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
824                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
825                     for (int i = 0; i < height; i++)
826                     {
827                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
828                         for (int j = 0; j < width; j++)
829                         {
830                             endianUtil.encodeInt(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
831                         }
832                     }
833                 }
834 
835                 @Override
836                 public int[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
837                         throws SerializationException
838                 {
839                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
840                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
841                     int[][] result = new int[height][width];
842                     for (int i = 0; i < height; i++)
843                     {
844                         for (int j = 0; j < width; j++)
845                         {
846                             result[i][j] = endianUtil.decodeInt(buffer, pointer.getAndIncrement(getElementSize()));
847                         }
848                     }
849                     return result;
850                 }
851             };
852 
853     /** Converter for Integer matrix. */
854     private static final Serializer<Integer[][]> CONVERT_INTEGER_MATRIX =
855             new ObjectMatrixSerializer<Integer>(FieldTypes.INT_32_MATRIX, 4, Integer.class, "Integer_32_matrix")
856             {
857                 @Override
858                 public void serializeElement(final Integer object, final byte[] buffer, final int offset,
859                         final EndianUtil endianUtil)
860                 {
861                     endianUtil.encodeInt(object, buffer, offset);
862                 }
863 
864                 @Override
865                 public Integer deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
866                 {
867                     return endianUtil.decodeInt(buffer, offset);
868                 }
869             };
870 
871     /** Converter for long matrix. */
872     private static final Serializer<long[][]> CONVERT_LNG_MATRIX =
873             new BasicPrimitiveArrayOrMatrixSerializer<long[][]>(FieldTypes.LONG_64_MATRIX, 8, "long_64_matrix", 2)
874             {
875                 @Override
876                 public int size(final long[][] matrix)
877                 {
878                     return 8 + getElementSize() * matrix.length * matrix[0].length;
879                 }
880 
881                 @Override
882                 public void serialize(final long[][] matrix, final byte[] buffer, final Pointer pointer,
883                         final EndianUtil endianUtil) throws SerializationException
884                 {
885                     int height = matrix.length;
886                     int width = matrix[0].length;
887                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
888                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
889                     for (int i = 0; i < height; i++)
890                     {
891                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
892                         for (int j = 0; j < width; j++)
893                         {
894                             endianUtil.encodeLong(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
895                         }
896                     }
897                 }
898 
899                 @Override
900                 public long[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
901                         throws SerializationException
902                 {
903                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
904                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
905                     long[][] result = new long[height][width];
906                     for (int i = 0; i < height; i++)
907                     {
908                         for (int j = 0; j < width; j++)
909                         {
910                             result[i][j] = endianUtil.decodeLong(buffer, pointer.getAndIncrement(getElementSize()));
911                         }
912                     }
913                     return result;
914                 }
915             };
916 
917     /** Converter for Long matrix. */
918     private static final Serializer<Long[][]> CONVERT_LONG_MATRIX =
919             new ObjectMatrixSerializer<Long>(FieldTypes.LONG_64_MATRIX, 8, Long.class, "Long_64_matrix")
920             {
921                 @Override
922                 public void serializeElement(final Long object, final byte[] buffer, final int offset,
923                         final EndianUtil endianUtil)
924                 {
925                     endianUtil.encodeLong(object, buffer, offset);
926                 }
927 
928                 @Override
929                 public Long deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
930                 {
931                     return endianUtil.decodeLong(buffer, offset);
932                 }
933             };
934 
935     /** Converter for float matrix. */
936     private static final Serializer<float[][]> CONVERT_FLT_MATRIX =
937             new BasicPrimitiveArrayOrMatrixSerializer<float[][]>(FieldTypes.FLOAT_32_MATRIX, 4, "float_32_matrix", 2)
938             {
939                 @Override
940                 public int size(final float[][] matrix)
941                 {
942                     return 8 + getElementSize() * matrix.length * matrix[0].length;
943                 }
944 
945                 @Override
946                 public void serialize(final float[][] matrix, final byte[] buffer, final Pointer pointer,
947                         final EndianUtil endianUtil) throws SerializationException
948                 {
949                     int height = matrix.length;
950                     int width = matrix[0].length;
951                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
952                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
953                     for (int i = 0; i < height; i++)
954                     {
955                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
956                         for (int j = 0; j < width; j++)
957                         {
958                             endianUtil.encodeFloat(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
959                         }
960                     }
961                 }
962 
963                 @Override
964                 public float[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
965                         throws SerializationException
966                 {
967                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
968                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
969                     float[][] result = new float[height][width];
970                     for (int i = 0; i < height; i++)
971                     {
972                         for (int j = 0; j < width; j++)
973                         {
974                             result[i][j] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(getElementSize()));
975                         }
976                     }
977                     return result;
978                 }
979             };
980 
981     /** Converter for Float matrix. */
982     private static final Serializer<Float[][]> CONVERT_FLOAT_MATRIX =
983             new ObjectMatrixSerializer<Float>(FieldTypes.FLOAT_32_MATRIX, 4, Float.class, "Float_32_matrix")
984             {
985                 @Override
986                 public void serializeElement(final Float object, final byte[] buffer, final int offset,
987                         final EndianUtil endianUtil)
988                 {
989                     endianUtil.encodeFloat(object, buffer, offset);
990                 }
991 
992                 @Override
993                 public Float deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
994                 {
995                     return endianUtil.decodeFloat(buffer, offset);
996                 }
997             };
998 
999     /** Converter for double matrix. */
1000     private static final Serializer<double[][]> CONVERT_DBL_MATRIX =
1001             new BasicPrimitiveArrayOrMatrixSerializer<double[][]>(FieldTypes.DOUBLE_64_MATRIX, 8, "double_64_matrix", 2)
1002             {
1003                 @Override
1004                 public int size(final double[][] matrix)
1005                 {
1006                     return 8 + getElementSize() * matrix.length * matrix[0].length;
1007                 }
1008 
1009                 @Override
1010                 public void serialize(final double[][] matrix, final byte[] buffer, final Pointer pointer,
1011                         final EndianUtil endianUtil) throws SerializationException
1012                 {
1013                     int height = matrix.length;
1014                     int width = matrix[0].length;
1015                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1016                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
1017                     for (int i = 0; i < height; i++)
1018                     {
1019                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
1020                         for (int j = 0; j < width; j++)
1021                         {
1022                             endianUtil.encodeDouble(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
1023                         }
1024                     }
1025                 }
1026 
1027                 @Override
1028                 public double[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1029                         throws SerializationException
1030                 {
1031                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1032                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1033                     double[][] result = new double[height][width];
1034                     for (int i = 0; i < height; i++)
1035                     {
1036                         for (int j = 0; j < width; j++)
1037                         {
1038                             result[i][j] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(getElementSize()));
1039                         }
1040                     }
1041                     return result;
1042                 }
1043             };
1044 
1045     /** Converter for Double matrix. */
1046     private static final Serializer<Double[][]> CONVERT_DOUBLE_MATRIX =
1047             new ObjectMatrixSerializer<Double>(FieldTypes.DOUBLE_64_MATRIX, 8, Double.class, "Double_64_matrix")
1048             {
1049                 @Override
1050                 public void serializeElement(final Double object, final byte[] buffer, final int offset,
1051                         final EndianUtil endianUtil)
1052                 {
1053                     endianUtil.encodeDouble(object, buffer, offset);
1054                 }
1055 
1056                 @Override
1057                 public Double deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
1058                 {
1059                     return endianUtil.decodeDouble(buffer, offset);
1060                 }
1061             };
1062 
1063     /** Converter for boolean matrix. */
1064     private static final Serializer<boolean[][]> CONVERT_BOOL_MATRIX =
1065             new BasicPrimitiveArrayOrMatrixSerializer<boolean[][]>(FieldTypes.BOOLEAN_8_MATRIX, 1, "boolean_8_matrix", 2)
1066             {
1067                 @Override
1068                 public int size(final boolean[][] matrix)
1069                 {
1070                     return 8 + getElementSize() * matrix.length * matrix[0].length;
1071                 }
1072 
1073                 @Override
1074                 public void serialize(final boolean[][] matrix, final byte[] buffer, final Pointer pointer,
1075                         final EndianUtil endianUtil) throws SerializationException
1076                 {
1077                     int height = matrix.length;
1078                     int width = matrix[0].length;
1079                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1080                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
1081                     for (int i = 0; i < height; i++)
1082                     {
1083                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
1084                         for (int j = 0; j < width; j++)
1085                         {
1086                             buffer[pointer.getAndIncrement(getElementSize())] = (byte) (matrix[i][j] ? 1 : 0);
1087                         }
1088                     }
1089                 }
1090 
1091                 @Override
1092                 public boolean[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1093                         throws SerializationException
1094                 {
1095                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1096                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1097                     boolean[][] result = new boolean[height][width];
1098                     for (int i = 0; i < height; i++)
1099                     {
1100                         for (int j = 0; j < width; j++)
1101                         {
1102                             result[i][j] = buffer[pointer.getAndIncrement(getElementSize())] == 0 ? false : true;
1103                         }
1104                     }
1105                     return result;
1106                 }
1107             };
1108 
1109     /** Converter for Boolean matrix. */
1110     private static final Serializer<Boolean[][]> CONVERT_BOOLEAN_MATRIX =
1111             new ObjectMatrixSerializer<Boolean>(FieldTypes.BOOLEAN_8_MATRIX, 1, Boolean.class, "Boolean_8_matrix")
1112             {
1113                 @Override
1114                 public void serializeElement(final Boolean object, final byte[] buffer, final int offset,
1115                         final EndianUtil endianUtil)
1116                 {
1117                     buffer[offset] = (byte) (object ? 1 : 0);
1118                 }
1119 
1120                 @Override
1121                 public Boolean deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
1122                 {
1123                     return buffer[offset] == 0 ? false : true;
1124                 }
1125             };
1126 
1127     /** Converter for descendants of AbstractFloatScalar. */
1128     private static final Serializer<AbstractFloatScalar<?, ?>> CONVERT_DJUNITS_FLOAT_SCALAR =
1129             new ObjectSerializer<AbstractFloatScalar<?, ?>>(FieldTypes.FLOAT_32_UNIT, "Djunits_FloatScalar")
1130             {
1131                 @Override
1132                 public int size(final AbstractFloatScalar<?, ?> afs) throws SerializationException
1133                 {
1134                     return 2 + extraBytesMoney(afs) + 4;
1135                 }
1136 
1137                 @Override
1138                 public void serialize(final AbstractFloatScalar<?, ?> afs, final byte[] buffer, final Pointer pointer,
1139                         final EndianUtil endianUtil) throws SerializationException
1140                 {
1141                     encodeUnit(afs.getUnit(), buffer, pointer, endianUtil);
1142                     float v = afs.si;
1143                     endianUtil.encodeFloat(v, buffer, pointer.getAndIncrement(4));
1144                 }
1145 
1146                 @Override
1147                 public AbstractFloatScalar<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1148                         final EndianUtil endianUtil) throws SerializationException
1149                 {
1150                     Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1151                     return FloatScalarUtil.instantiateAnonymousSI(endianUtil.decodeFloat(buffer, pointer.getAndIncrement(4)),
1152                             unit);
1153                 }
1154             };
1155 
1156     /** Converter for descendants of AbstractDoubleScalar. */
1157     private static final Serializer<AbstractDoubleScalar<?, ?>> CONVERT_DJUNITS_DOUBLE_SCALAR =
1158             new ObjectSerializer<AbstractDoubleScalar<?, ?>>(FieldTypes.DOUBLE_64_UNIT, "Djunits_DoubleScalar")
1159             {
1160                 @Override
1161                 public int size(final AbstractDoubleScalar<?, ?> ads) throws SerializationException
1162                 {
1163                     return 2 + extraBytesMoney(ads) + 8;
1164                 }
1165 
1166                 @Override
1167                 public void serialize(final AbstractDoubleScalar<?, ?> ads, final byte[] buffer, final Pointer pointer,
1168                         final EndianUtil endianUtil) throws SerializationException
1169                 {
1170                     encodeUnit(ads.getUnit(), buffer, pointer, endianUtil);
1171                     double v = ads.si;
1172                     endianUtil.encodeDouble(v, buffer, pointer.getAndIncrement(8));
1173                 }
1174 
1175                 @Override
1176                 public AbstractDoubleScalar<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1177                         final EndianUtil endianUtil) throws SerializationException
1178                 {
1179                     Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1180                     return DoubleScalarUtil.instantiateAnonymousSI(endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8)),
1181                             unit);
1182                 }
1183             };
1184 
1185     /** Converter for descendants of AbstractFloatVector. */
1186     private static final Serializer<AbstractFloatVector<?, ?>> CONVERT_DJUNITS_FLOAT_VECTOR =
1187             new DjunitsArrayOrMatrixSerializer<AbstractFloatVector<?, ?>>(FieldTypes.FLOAT_32_UNIT_ARRAY, "Djunits_FloatVector",
1188                     1)
1189             {
1190                 @Override
1191                 public int size(final AbstractFloatVector<?, ?> afv) throws SerializationException
1192                 {
1193                     try
1194                     {
1195                         return 4 + 2 + extraBytesMoney(afv.get(0)) + 4 * afv.size();
1196                     }
1197                     catch (ValueException e)
1198                     {
1199                         throw new SerializationException(e);
1200                     }
1201                 }
1202 
1203                 @Override
1204                 public void serialize(final AbstractFloatVector<?, ?> afv, final byte[] buffer, final Pointer pointer,
1205                         final EndianUtil endianUtil) throws SerializationException
1206                 {
1207                     try
1208                     {
1209                         endianUtil.encodeInt(afv.size(), buffer, pointer.getAndIncrement(4));
1210                         encodeUnit(afv.getUnit(), buffer, pointer, endianUtil);
1211                         for (int i = 0; i < afv.size(); i++)
1212                         {
1213                             endianUtil.encodeFloat(afv.get(i).si, buffer, pointer.getAndIncrement(4));
1214                         }
1215                     }
1216                     catch (ValueException e)
1217                     {
1218                         throw new SerializationException(e);
1219                     }
1220                 }
1221 
1222                 @Override
1223                 public AbstractFloatVector<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1224                         final EndianUtil endianUtil) throws SerializationException
1225                 {
1226                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1227                     Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1228                     float[] array = new float[size];
1229                     for (int i = 0; i < size; i++)
1230                     {
1231                         array[i] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(4));
1232                     }
1233                     try
1234                     {
1235                         return FloatVectorUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE);
1236                     }
1237                     catch (ValueException exception)
1238                     {
1239                         throw new SerializationException(exception);
1240                     }
1241                 }
1242             };
1243 
1244     /** Converter for descendants of AbstractDoubleVector. */
1245     private static final Serializer<AbstractDoubleVector<?, ?>> CONVERT_DJUNITS_DOUBLE_VECTOR =
1246             new DjunitsArrayOrMatrixSerializer<AbstractDoubleVector<?, ?>>(FieldTypes.DOUBLE_64_UNIT_ARRAY,
1247                     "Djunits_DoubleVector", 1)
1248             {
1249                 @Override
1250                 public int size(final AbstractDoubleVector<?, ?> adv) throws SerializationException
1251                 {
1252                     try
1253                     {
1254                         return 4 + 2 + extraBytesMoney(adv.get(0)) + 8 * adv.size();
1255                     }
1256                     catch (ValueException e)
1257                     {
1258                         throw new SerializationException(e);
1259                     }
1260                 }
1261 
1262                 @Override
1263                 public void serialize(final AbstractDoubleVector<?, ?> adv, final byte[] buffer, final Pointer pointer,
1264                         final EndianUtil endianUtil) throws SerializationException
1265                 {
1266                     try
1267                     {
1268                         endianUtil.encodeInt(adv.size(), buffer, pointer.getAndIncrement(4));
1269                         encodeUnit(adv.getUnit(), buffer, pointer, endianUtil);
1270                         for (int i = 0; i < adv.size(); i++)
1271                         {
1272                             endianUtil.encodeDouble(adv.get(i).si, buffer, pointer.getAndIncrement(8));
1273                         }
1274                     }
1275                     catch (ValueException e)
1276                     {
1277                         throw new SerializationException(e);
1278                     }
1279                 }
1280 
1281                 @Override
1282                 public AbstractDoubleVector<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1283                         final EndianUtil endianUtil) throws SerializationException
1284                 {
1285                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1286                     Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1287                     double[] array = new double[size];
1288                     for (int i = 0; i < size; i++)
1289                     {
1290                         array[i] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8));
1291                     }
1292                     try
1293                     {
1294                         return DoubleVectorUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE);
1295                     }
1296                     catch (ValueException exception)
1297                     {
1298                         throw new SerializationException(exception);
1299                     }
1300                 }
1301             };
1302 
1303     /** Converter for descendants of AbstractFloatMatrix. */
1304     private static final Serializer<AbstractFloatMatrix<?, ?>> CONVERT_DJUNITS_FLOAT_MATRIX =
1305             new DjunitsArrayOrMatrixSerializer<AbstractFloatMatrix<?, ?>>(FieldTypes.FLOAT_32_UNIT_MATRIX,
1306                     "Djunits_FloatMatrix", 2)
1307             {
1308                 @Override
1309                 public int size(final AbstractFloatMatrix<?, ?> afm) throws SerializationException
1310                 {
1311                     try
1312                     {
1313                         return 4 + 4 + 2 + extraBytesMoney(afm.get(0, 0)) + 4 * afm.rows() * afm.columns();
1314                     }
1315                     catch (ValueException e)
1316                     {
1317                         throw new SerializationException(e);
1318                     }
1319                 }
1320 
1321                 @Override
1322                 public void serialize(final AbstractFloatMatrix<?, ?> afm, final byte[] buffer, final Pointer pointer,
1323                         final EndianUtil endianUtil) throws SerializationException
1324                 {
1325                     try
1326                     {
1327                         endianUtil.encodeInt(afm.rows(), buffer, pointer.getAndIncrement(4));
1328                         endianUtil.encodeInt(afm.columns(), buffer, pointer.getAndIncrement(4));
1329                         encodeUnit(afm.getUnit(), buffer, pointer, endianUtil);
1330                         for (int i = 0; i < afm.rows(); i++)
1331                         {
1332                             for (int j = 0; j < afm.columns(); j++)
1333                             {
1334                                 endianUtil.encodeFloat(afm.get(i, j).si, buffer, pointer.getAndIncrement(4));
1335                             }
1336                         }
1337                     }
1338                     catch (ValueException e)
1339                     {
1340                         throw new SerializationException(e);
1341                     }
1342                 }
1343 
1344                 @Override
1345                 public AbstractFloatMatrix<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1346                         final EndianUtil endianUtil) throws SerializationException
1347                 {
1348                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1349                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1350                     Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1351                     float[][] array = new float[height][width];
1352                     for (int i = 0; i < height; i++)
1353                     {
1354                         for (int j = 0; j < width; j++)
1355                         {
1356                             array[i][j] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(4));
1357                         }
1358                     }
1359                     try
1360                     {
1361                         return FloatMatrixUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE);
1362                     }
1363                     catch (ValueException exception)
1364                     {
1365                         throw new SerializationException(exception);
1366                     }
1367                 }
1368             };
1369 
1370     /** Converter for descendants of AbstractDoubleMatrix. */
1371     private static final Serializer<AbstractDoubleMatrix<?, ?>> CONVERT_DJUNITS_DOUBLE_MATRIX =
1372             new DjunitsArrayOrMatrixSerializer<AbstractDoubleMatrix<?, ?>>(FieldTypes.DOUBLE_64_UNIT_MATRIX,
1373                     "Djunits_DoubleMatrix", 2)
1374             {
1375                 @Override
1376                 public int size(final AbstractDoubleMatrix<?, ?> adm) throws SerializationException
1377                 {
1378                     try
1379                     {
1380                         return 4 + 4 + 2 + extraBytesMoney(adm.get(0, 0)) + 8 * adm.rows() * adm.columns();
1381                     }
1382                     catch (ValueException e)
1383                     {
1384                         throw new SerializationException(e);
1385                     }
1386                 }
1387 
1388                 @Override
1389                 public void serialize(final AbstractDoubleMatrix<?, ?> adm, final byte[] buffer, final Pointer pointer,
1390                         final EndianUtil endianUtil) throws SerializationException
1391                 {
1392                     try
1393                     {
1394                         endianUtil.encodeInt(adm.rows(), buffer, pointer.getAndIncrement(4));
1395                         endianUtil.encodeInt(adm.columns(), buffer, pointer.getAndIncrement(4));
1396                         encodeUnit(adm.getUnit(), buffer, pointer, endianUtil);
1397                         for (int i = 0; i < adm.rows(); i++)
1398                         {
1399                             for (int j = 0; j < adm.columns(); j++)
1400                             {
1401                                 endianUtil.encodeDouble(adm.get(i, j).si, buffer, pointer.getAndIncrement(8));
1402                             }
1403                         }
1404                     }
1405                     catch (ValueException e)
1406                     {
1407                         throw new SerializationException(e);
1408                     }
1409                 }
1410 
1411                 @Override
1412                 public AbstractDoubleMatrix<?, ?> deSerialize(final byte[] buffer, final Pointer pointer,
1413                         final EndianUtil endianUtil) throws SerializationException
1414                 {
1415                     try
1416                     {
1417                         int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1418                         int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1419                         Unit<? extends Unit<?>> unit = getUnit(buffer, pointer, endianUtil);
1420                         double[][] array = new double[height][width];
1421                         for (int i = 0; i < height; i++)
1422                         {
1423                             for (int j = 0; j < width; j++)
1424                             {
1425                                 array[i][j] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8));
1426                             }
1427                         }
1428                         return DoubleMatrixUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE);
1429                     }
1430                     catch (ValueException exception)
1431                     {
1432                         throw new SerializationException(exception);
1433                     }
1434                 }
1435             };
1436 
1437     /** Serializer for array of DoubleVector. Each DoubleVector must have same size. */
1438     private static final Serializer<AbstractDoubleVector<?, ?>[]> CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY =
1439             new ObjectSerializer<AbstractDoubleVector<?, ?>[]>(FieldTypes.DOUBLE_64_UNIT_COLUMN_ARRAY, "Djunits_vector_array")
1440             {
1441 
1442                 @Override
1443                 public int size(final AbstractDoubleVector<?, ?>[] adva) throws SerializationException
1444                 {
1445                     int result = 4 + 4;
1446                     int width = adva.length;
1447                     int height = adva[0].size();
1448                     for (int i = 0; i < width; i++)
1449                     {
1450                         AbstractDoubleVector<?, ?> adv = adva[i];
1451                         Throw.when(adv.size() != height, SerializationException.class,
1452                                 "All AbstractDoubleVectors in array must have same size");
1453                         try
1454                         {
1455                             result += 2 + extraBytesMoney(adv.get(0));
1456                         }
1457                         catch (ValueException e)
1458                         {
1459                             throw new SerializationException(e);
1460                         }
1461                     }
1462                     result += height * width * 8;
1463                     return result;
1464                 }
1465 
1466                 @Override
1467                 public void serialize(final AbstractDoubleVector<?, ?>[] adva, final byte[] buffer, final Pointer pointer,
1468                         final EndianUtil endianUtil) throws SerializationException
1469                 {
1470                     int width = adva.length;
1471                     int height = adva[0].size();
1472                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1473                     endianUtil.encodeInt(adva.length, buffer, pointer.getAndIncrement(4));
1474                     for (int i = 0; i < width; i++)
1475                     {
1476                         AbstractDoubleVector<?, ?> adv = adva[i];
1477                         Throw.when(adv.size() != height, SerializationException.class,
1478                                 "All AbstractDoubleVectors in array must have same size");
1479                         encodeUnit(adv.getUnit(), buffer, pointer, endianUtil);
1480                     }
1481                     for (int row = 0; row < height; row++)
1482                     {
1483                         for (int col = 0; col < width; col++)
1484                         {
1485                             try
1486                             {
1487                                 endianUtil.encodeDouble(adva[col].getSI(row), buffer, pointer.getAndIncrement(8));
1488                             }
1489                             catch (ValueException e)
1490                             {
1491                                 throw new SerializationException(e);
1492                             }
1493                         }
1494                     }
1495 
1496                 }
1497 
1498                 @Override
1499                 public AbstractDoubleVector<?, ?>[] deSerialize(final byte[] buffer, final Pointer pointer,
1500                         final EndianUtil endianUtil) throws SerializationException
1501                 {
1502                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1503                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1504                     AbstractDoubleVector<?, ?>[] result = new AbstractDoubleVector<?, ?>[width];
1505                     Unit<? extends Unit<?>>[] units = new Unit<?>[width];
1506                     for (int col = 0; col < width; col++)
1507                     {
1508                         units[col] = getUnit(buffer, pointer, endianUtil);
1509                     }
1510                     double[][] values = new double[width][height];
1511                     for (int row = 0; row < height; row++)
1512                     {
1513                         for (int col = 0; col < width; col++)
1514                         {
1515                             values[col][row] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8));
1516                         }
1517                     }
1518                     for (int col = 0; col < width; col++)
1519                     {
1520                         try
1521                         {
1522                             result[col] = DoubleVectorUtil.instantiateAnonymousSI(values[col], units[col], StorageType.DENSE);
1523                         }
1524                         catch (ValueException e)
1525                         {
1526                             throw new SerializationException(e);
1527                         }
1528                     }
1529                     return result;
1530                 }
1531             };
1532 
1533     /** Converter for array of SerializebleObject using UTF16 for strings and characters. */
1534     private static final Serializer<SerializableObject<?>[]> COMPOUND_ARRAY_SERIALIZER_UTF16 =
1535             new ObjectSerializer<SerializableObject<?>[]>((byte) 120, "Compound")
1536             {
1537 
1538                 @SuppressWarnings({"unchecked", "rawtypes"})
1539                 @Override
1540                 public int size(final SerializableObject<?>[] objects) throws SerializationException
1541                 {
1542                     int result = 4 + 4;
1543                     SerializableObject<?> so = objects[0];
1544                     Object[] objectArray = so.exportAsList().toArray();
1545                     Serializer[] serializers = buildEncoderList(false, objectArray);
1546                     result += serializers.length;
1547                     // TODO this assumes that objectArray does not contain money types that use a multi-byte field type
1548                     for (int i = 0; i < objectArray.length; i++)
1549                     {
1550                         result += objects.length * serializers[i].size(objectArray[i]);
1551                     }
1552                     return result;
1553                 }
1554 
1555                 @SuppressWarnings({"unchecked", "rawtypes"})
1556                 @Override
1557                 public void serialize(final SerializableObject<?>[] objects, final byte[] buffer, final Pointer pointer,
1558                         final EndianUtil endianUtil) throws SerializationException
1559                 {
1560                     SerializableObject<?> so = objects[0];
1561                     Object[] objectArray = so.exportAsList().toArray();
1562                     endianUtil.encodeInt(objects.length, buffer, pointer.getAndIncrement(4));
1563                     endianUtil.encodeInt(objectArray.length, buffer, pointer.getAndIncrement(4));
1564                     Serializer[] serializers = buildEncoderList(false, objectArray);
1565                     for (int i = 0; i < objectArray.length; i++)
1566                     {
1567                         buffer[pointer.getAndIncrement(1)] = serializers[i].fieldType();
1568                     }
1569                     for (int i = 0; i < objects.length; i++)
1570                     {
1571                         List<Object> row = objects[i].exportAsList();
1572                         Throw.when(row.size() != objectArray.length, SerializationException.class,
1573                                 "List in row %d has %d elements which differs from the %d elements in row 0", i, row.size(),
1574                                 objectArray.length);
1575                         for (int j = 0; j < row.size(); j++)
1576                         {
1577                             serializers[j].serialize(row.get(j), buffer, pointer, endianUtil);
1578                         }
1579                     }
1580                 }
1581 
1582                 @Override
1583                 public SerializableObject<?>[] deSerialize(final byte[] buffer, final Pointer pointer,
1584                         final EndianUtil endianUtil) throws SerializationException
1585                 {
1586                     int arraySize = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1587                     int fieldCount = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1588                     Serializer<?>[] deSerializers = new Serializer[fieldCount];
1589                     for (int i = 0; i < fieldCount; i++)
1590                     {
1591                         Byte key = buffer[pointer.getAndIncrement(1)];
1592                         Serializer<?> deSerializer = PRIMITIVE_DATA_DECODERS.get(key);
1593                         Throw.whenNull(SerializationException.class, "No decoder for %d", key);
1594                         deSerializers[i] = deSerializer;
1595                     }
1596                     MinimalSerializableObject[] result = new MinimalSerializableObject[arraySize];
1597                     for (int i = 0; i < arraySize; i++)
1598                     {
1599                         List<Object> element = new ArrayList<>();
1600                         for (int j = 0; j < fieldCount; j++)
1601                         {
1602                             element.add(deSerializers[j].deSerialize(buffer, pointer, endianUtil));
1603                         }
1604                         result[i] = new MinimalSerializableObject(element);
1605                     }
1606                     return result;
1607                 }
1608             };
1609 
1610     /** Converter for array of SerializebleObject using UTF8 for strings and characters. */
1611     private static final Serializer<SerializableObject<?>[]> COMPOUND_ARRAY_SERIALIZER_UTF8 =
1612             new ObjectSerializer<SerializableObject<?>[]>((byte) 121, "Compound")
1613             {
1614 
1615                 @SuppressWarnings({"unchecked", "rawtypes"})
1616                 @Override
1617                 public int size(final SerializableObject<?>[] objects) throws SerializationException
1618                 {
1619                     int result = 4 + 4;
1620                     SerializableObject<?> so = objects[0];
1621                     Object[] objectArray = so.exportAsList().toArray();
1622                     Serializer[] serializers = buildEncoderList(true, objectArray);
1623                     result += serializers.length;
1624                     // TODO this assumes that objectArray does not contain money types that use a multi-byte field type
1625                     for (int i = 0; i < objectArray.length; i++)
1626                     {
1627                         result += objects.length * serializers[i].size(objectArray[i]);
1628                     }
1629                     return result;
1630                 }
1631 
1632                 @SuppressWarnings({"unchecked", "rawtypes"})
1633                 @Override
1634                 public void serialize(final SerializableObject<?>[] objects, final byte[] buffer, final Pointer pointer,
1635                         final EndianUtil endianUtil) throws SerializationException
1636                 {
1637                     SerializableObject<?> so = objects[0];
1638                     Object[] objectArray = so.exportAsList().toArray();
1639                     endianUtil.encodeInt(objects.length, buffer, pointer.getAndIncrement(4));
1640                     endianUtil.encodeInt(objectArray.length, buffer, pointer.getAndIncrement(4));
1641                     Serializer[] serializers = buildEncoderList(true, objectArray);
1642                     for (int i = 0; i < objectArray.length; i++)
1643                     {
1644                         buffer[pointer.getAndIncrement(1)] = serializers[i].fieldType();
1645                     }
1646                     for (int i = 0; i < objects.length; i++)
1647                     {
1648                         List<Object> row = objects[i].exportAsList();
1649                         Throw.when(row.size() != objectArray.length, SerializationException.class,
1650                                 "List in row %d has %d elements which differs from the %d elements in row 0", i, row.size(),
1651                                 objectArray.length);
1652                         for (int j = 0; j < row.size(); j++)
1653                         {
1654                             serializers[j].serialize(row.get(j), buffer, pointer, endianUtil);
1655                         }
1656                     }
1657                 }
1658 
1659                 @Override
1660                 public SerializableObject<?>[] deSerialize(final byte[] buffer, final Pointer pointer,
1661                         final EndianUtil endianUtil) throws SerializationException
1662                 {
1663                     int arraySize = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1664                     int fieldCount = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1665                     Serializer<?>[] deSerializers = new Serializer[fieldCount];
1666                     for (int i = 0; i < fieldCount; i++)
1667                     {
1668                         Byte key = buffer[pointer.getAndIncrement(1)];
1669                         Serializer<?> deSerializer = PRIMITIVE_DATA_DECODERS.get(key);
1670                         Throw.whenNull(SerializationException.class, "No decoder for %d", key);
1671                         deSerializers[i] = deSerializer;
1672                     }
1673                     MinimalSerializableObject[] result = new MinimalSerializableObject[arraySize];
1674                     for (int i = 0; i < arraySize; i++)
1675                     {
1676                         List<Object> element = new ArrayList<>();
1677                         for (int j = 0; j < fieldCount; j++)
1678                         {
1679                             element.add(deSerializers[j].deSerialize(buffer, pointer, endianUtil));
1680                         }
1681                         result[i] = new MinimalSerializableObject(element);
1682                     }
1683                     return result;
1684                 }
1685             };
1686 
1687     static
1688     {
1689         ENCODERS.put(Byte.class, CONVERT_BYTE);
1690         ENCODERS.put(byte.class, CONVERT_BYTE);
1691         ENCODERS.put(Short.class, CONVERT_SHORT);
1692         ENCODERS.put(short.class, CONVERT_SHORT);
1693         ENCODERS.put(Integer.class, CONVERT_INTEGER);
1694         ENCODERS.put(int.class, CONVERT_INTEGER);
1695         ENCODERS.put(Long.class, CONVERT_LONG);
1696         ENCODERS.put(long.class, CONVERT_LONG);
1697         ENCODERS.put(Float.class, CONVERT_FLOAT);
1698         ENCODERS.put(float.class, CONVERT_FLOAT);
1699         ENCODERS.put(Double.class, CONVERT_DOUBLE);
1700         ENCODERS.put(double.class, CONVERT_DOUBLE);
1701         ENCODERS.put(Boolean.class, CONVERT_BOOLEAN);
1702         ENCODERS.put(boolean.class, CONVERT_BOOLEAN);
1703         ENCODERS.put(Byte[].class, CONVERT_BYTE_ARRAY);
1704         ENCODERS.put(byte[].class, CONVERT_BT_ARRAY);
1705         ENCODERS.put(Short[].class, CONVERT_SHORT_ARRAY);
1706         ENCODERS.put(short[].class, CONVERT_SHRT_ARRAY);
1707         ENCODERS.put(Integer[].class, CONVERT_INTEGER_ARRAY);
1708         ENCODERS.put(int[].class, CONVERT_INT_ARRAY);
1709         ENCODERS.put(Long[].class, CONVERT_LONG_ARRAY);
1710         ENCODERS.put(long[].class, CONVERT_LNG_ARRAY);
1711         ENCODERS.put(Float[].class, CONVERT_FLOAT_ARRAY);
1712         ENCODERS.put(float[].class, CONVERT_FLT_ARRAY);
1713         ENCODERS.put(Double[].class, CONVERT_DOUBLE_ARRAY);
1714         ENCODERS.put(double[].class, CONVERT_DBL_ARRAY);
1715         ENCODERS.put(Boolean[].class, CONVERT_BOOLEAN_ARRAY);
1716         ENCODERS.put(boolean[].class, CONVERT_BOOL_ARRAY);
1717         ENCODERS.put(Byte[][].class, CONVERT_BYTE_MATRIX);
1718         ENCODERS.put(byte[][].class, CONVERT_BT_MATRIX);
1719         ENCODERS.put(Short[][].class, CONVERT_SHORT_MATRIX);
1720         ENCODERS.put(short[][].class, CONVERT_SHRT_MATRIX);
1721         ENCODERS.put(Integer[][].class, CONVERT_INTEGER_MATRIX);
1722         ENCODERS.put(int[][].class, CONVERT_INT_MATRIX);
1723         ENCODERS.put(Long[][].class, CONVERT_LONG_MATRIX);
1724         ENCODERS.put(long[][].class, CONVERT_LNG_MATRIX);
1725         ENCODERS.put(Float[][].class, CONVERT_FLOAT_MATRIX);
1726         ENCODERS.put(float[][].class, CONVERT_FLT_MATRIX);
1727         ENCODERS.put(Double[][].class, CONVERT_DOUBLE_MATRIX);
1728         ENCODERS.put(double[][].class, CONVERT_DBL_MATRIX);
1729         ENCODERS.put(Boolean[][].class, CONVERT_BOOLEAN_MATRIX);
1730         ENCODERS.put(boolean[][].class, CONVERT_BOOL_MATRIX);
1731 
1732         PRIMITIVE_DATA_DECODERS.put(CONVERT_BYTE.fieldType(), CONVERT_BYTE);
1733         PRIMITIVE_DATA_DECODERS.put(CONVERT_CHARACTER8.fieldType(), CONVERT_CHARACTER8);
1734         PRIMITIVE_DATA_DECODERS.put(CONVERT_CHARACTER16.fieldType(), CONVERT_CHARACTER16);
1735         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHORT.fieldType(), CONVERT_SHORT);
1736         PRIMITIVE_DATA_DECODERS.put(CONVERT_INTEGER.fieldType(), CONVERT_INTEGER);
1737         PRIMITIVE_DATA_DECODERS.put(CONVERT_LONG.fieldType(), CONVERT_LONG);
1738         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLOAT.fieldType(), CONVERT_FLOAT);
1739         PRIMITIVE_DATA_DECODERS.put(CONVERT_DOUBLE.fieldType(), CONVERT_DOUBLE);
1740         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOLEAN.fieldType(), CONVERT_BOOLEAN);
1741         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING8.fieldType(), CONVERT_STRING8);
1742         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING16.fieldType(), CONVERT_STRING16);
1743         PRIMITIVE_DATA_DECODERS.put(CONVERT_BT_ARRAY.fieldType(), CONVERT_BT_ARRAY);
1744         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHRT_ARRAY.fieldType(), CONVERT_SHRT_ARRAY);
1745         PRIMITIVE_DATA_DECODERS.put(CONVERT_INT_ARRAY.fieldType(), CONVERT_INT_ARRAY);
1746         PRIMITIVE_DATA_DECODERS.put(CONVERT_LNG_ARRAY.fieldType(), CONVERT_LNG_ARRAY);
1747         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLT_ARRAY.fieldType(), CONVERT_FLT_ARRAY);
1748         PRIMITIVE_DATA_DECODERS.put(CONVERT_DBL_ARRAY.fieldType(), CONVERT_DBL_ARRAY);
1749         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOL_ARRAY.fieldType(), CONVERT_BOOL_ARRAY);
1750         PRIMITIVE_DATA_DECODERS.put(CONVERT_BT_MATRIX.fieldType(), CONVERT_BT_MATRIX);
1751         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHRT_MATRIX.fieldType(), CONVERT_SHRT_MATRIX);
1752         PRIMITIVE_DATA_DECODERS.put(CONVERT_INT_MATRIX.fieldType(), CONVERT_INT_MATRIX);
1753         PRIMITIVE_DATA_DECODERS.put(CONVERT_LNG_MATRIX.fieldType(), CONVERT_LNG_MATRIX);
1754         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLT_MATRIX.fieldType(), CONVERT_FLT_MATRIX);
1755         PRIMITIVE_DATA_DECODERS.put(CONVERT_DBL_MATRIX.fieldType(), CONVERT_DBL_MATRIX);
1756         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOL_MATRIX.fieldType(), CONVERT_BOOL_MATRIX);
1757         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_SCALAR.fieldType(), CONVERT_DJUNITS_FLOAT_SCALAR);
1758         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_SCALAR.fieldType(), CONVERT_DJUNITS_DOUBLE_SCALAR);
1759         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_VECTOR.fieldType(), CONVERT_DJUNITS_FLOAT_VECTOR);
1760         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_VECTOR.fieldType(), CONVERT_DJUNITS_DOUBLE_VECTOR);
1761         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_MATRIX.fieldType(), CONVERT_DJUNITS_FLOAT_MATRIX);
1762         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_MATRIX.fieldType(), CONVERT_DJUNITS_DOUBLE_MATRIX);
1763         PRIMITIVE_DATA_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF16.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF16);
1764         PRIMITIVE_DATA_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF8.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF8);
1765         PRIMITIVE_DATA_DECODERS.put(CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY.fieldType(),
1766                 CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY);
1767 
1768         OBJECT_DECODERS.put(CONVERT_BYTE.fieldType(), CONVERT_BYTE);
1769         OBJECT_DECODERS.put(CONVERT_CHARACTER8.fieldType(), CONVERT_CHARACTER8);
1770         OBJECT_DECODERS.put(CONVERT_CHARACTER16.fieldType(), CONVERT_CHARACTER16);
1771         OBJECT_DECODERS.put(CONVERT_SHORT.fieldType(), CONVERT_SHORT);
1772         OBJECT_DECODERS.put(CONVERT_INTEGER.fieldType(), CONVERT_INTEGER);
1773         OBJECT_DECODERS.put(CONVERT_LONG.fieldType(), CONVERT_LONG);
1774         OBJECT_DECODERS.put(CONVERT_FLOAT.fieldType(), CONVERT_FLOAT);
1775         OBJECT_DECODERS.put(CONVERT_DOUBLE.fieldType(), CONVERT_DOUBLE);
1776         OBJECT_DECODERS.put(CONVERT_BOOLEAN.fieldType(), CONVERT_BOOLEAN);
1777         OBJECT_DECODERS.put(CONVERT_STRING8.fieldType(), CONVERT_STRING8);
1778         OBJECT_DECODERS.put(CONVERT_STRING16.fieldType(), CONVERT_STRING16);
1779         OBJECT_DECODERS.put(CONVERT_BYTE_ARRAY.fieldType(), CONVERT_BYTE_ARRAY);
1780         OBJECT_DECODERS.put(CONVERT_SHORT_ARRAY.fieldType(), CONVERT_SHORT_ARRAY);
1781         OBJECT_DECODERS.put(CONVERT_INTEGER_ARRAY.fieldType(), CONVERT_INTEGER_ARRAY);
1782         OBJECT_DECODERS.put(CONVERT_LONG_ARRAY.fieldType(), CONVERT_LONG_ARRAY);
1783         OBJECT_DECODERS.put(CONVERT_FLOAT_ARRAY.fieldType(), CONVERT_FLOAT_ARRAY);
1784         OBJECT_DECODERS.put(CONVERT_DOUBLE_ARRAY.fieldType(), CONVERT_DOUBLE_ARRAY);
1785         OBJECT_DECODERS.put(CONVERT_BOOLEAN_ARRAY.fieldType(), CONVERT_BOOLEAN_ARRAY);
1786         OBJECT_DECODERS.put(CONVERT_BYTE_MATRIX.fieldType(), CONVERT_BYTE_MATRIX);
1787         OBJECT_DECODERS.put(CONVERT_SHORT_MATRIX.fieldType(), CONVERT_SHORT_MATRIX);
1788         OBJECT_DECODERS.put(CONVERT_INTEGER_MATRIX.fieldType(), CONVERT_INTEGER_MATRIX);
1789         OBJECT_DECODERS.put(CONVERT_LONG_MATRIX.fieldType(), CONVERT_LONG_MATRIX);
1790         OBJECT_DECODERS.put(CONVERT_FLOAT_MATRIX.fieldType(), CONVERT_FLOAT_MATRIX);
1791         OBJECT_DECODERS.put(CONVERT_DOUBLE_MATRIX.fieldType(), CONVERT_DOUBLE_MATRIX);
1792         OBJECT_DECODERS.put(CONVERT_BOOLEAN_MATRIX.fieldType(), CONVERT_BOOLEAN_MATRIX);
1793         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_SCALAR.fieldType(), CONVERT_DJUNITS_FLOAT_SCALAR);
1794         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_SCALAR.fieldType(), CONVERT_DJUNITS_DOUBLE_SCALAR);
1795         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_VECTOR.fieldType(), CONVERT_DJUNITS_FLOAT_VECTOR);
1796         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_VECTOR.fieldType(), CONVERT_DJUNITS_DOUBLE_VECTOR);
1797         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_MATRIX.fieldType(), CONVERT_DJUNITS_FLOAT_MATRIX);
1798         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_MATRIX.fieldType(), CONVERT_DJUNITS_DOUBLE_MATRIX);
1799         OBJECT_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF16.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF16);
1800         OBJECT_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF8.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF8);
1801         OBJECT_DECODERS.put(CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY.fieldType(), CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY);
1802 
1803     }
1804 
1805     /** the UTF-8 charset. */
1806     protected static final Charset UTF8 = Charset.forName("UTF-8");
1807 
1808     /** the UTF-16 charset, big endian variant. */
1809     protected static final Charset UTF16 = Charset.forName("UTF-16BE");
1810 
1811     /**
1812      * Encode the object array into a byte[] message. Use UTF8 for the characters and for the String.
1813      * @param endianUtil EndianUtil; encoder to use for multi-byte values
1814      * @param content the objects to encode
1815      * @return the zeroMQ message to send as a byte array
1816      * @throws SerializationException on unknown data type
1817      */
1818     public static byte[] encodeUTF8(final EndianUtil endianUtil, final Object... content) throws SerializationException
1819     {
1820         return encode(true, endianUtil, content);
1821     }
1822 
1823     /**
1824      * Encode the object array into a byte[] message. Use UTF16 for the characters and for the String.
1825      * @param endianUtil EndianUtil; encoder for multi-byte values
1826      * @param content the objects to encode
1827      * @return the zeroMQ message to send as a byte array
1828      * @throws SerializationException on unknown data type
1829      */
1830     public static byte[] encodeUTF16(final EndianUtil endianUtil, final Object... content) throws SerializationException
1831     {
1832         return encode(false, endianUtil, content);
1833     }
1834 
1835     /**
1836      * Find the serializer for one object.
1837      * @param utf8 boolean; if true; use UTF8 encoding for characters and Strings; if false; use UTF16 encoding for characters
1838      *            and Strings
1839      * @param object Object; the object for which the serializer must be returned
1840      * @return Serializer; the serializer needed for <code>object</code>
1841      * @throws SerializationException when there is no known serializer for <code>object</code>
1842      */
1843     static Serializer<?> findEncoder(final boolean utf8, final Object object) throws SerializationException
1844     {
1845         Serializer<?> serializer = ENCODERS.get(object.getClass());
1846         if (serializer != null)
1847         {
1848             return serializer;
1849         }
1850         else if (object instanceof Character)
1851         {
1852             return utf8 ? CONVERT_CHARACTER8 : CONVERT_CHARACTER16;
1853         }
1854         else if (object instanceof String)
1855         {
1856             return utf8 ? CONVERT_STRING8 : CONVERT_STRING16;
1857         }
1858         else if (object instanceof AbstractFloatScalar)
1859         {
1860             return CONVERT_DJUNITS_FLOAT_SCALAR;
1861         }
1862         else if (object instanceof AbstractDoubleScalar)
1863         {
1864             return CONVERT_DJUNITS_DOUBLE_SCALAR;
1865         }
1866         else if (object instanceof AbstractFloatVector)
1867         {
1868             return CONVERT_DJUNITS_FLOAT_VECTOR;
1869         }
1870         else if (object instanceof AbstractDoubleVector)
1871         {
1872             return CONVERT_DJUNITS_DOUBLE_VECTOR;
1873         }
1874         else if (object instanceof AbstractFloatMatrix)
1875         {
1876             return CONVERT_DJUNITS_FLOAT_MATRIX;
1877         }
1878         else if (object instanceof AbstractDoubleMatrix)
1879         {
1880             return CONVERT_DJUNITS_DOUBLE_MATRIX;
1881         }
1882         else if (object instanceof SerializableObject[])
1883         {
1884             return utf8 ? COMPOUND_ARRAY_SERIALIZER_UTF8 : COMPOUND_ARRAY_SERIALIZER_UTF16;
1885         }
1886         else if (object instanceof AbstractDoubleVector[])
1887         {
1888             return CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY;
1889         }
1890         else
1891         {
1892             throw new SerializationException("Unhandled data type " + object.getClass());
1893         }
1894     }
1895 
1896     /**
1897      * Build the list of serializers corresponding to the data in an Object array.
1898      * @param utf8 boolean; if true; use UTF8 encoding for characters and Strings; if false; use UTF16 encoding for characters
1899      *            and Strings
1900      * @param content Object[]; the objects for which the serializers must be returned
1901      * @return Serializer[]; array filled with the serializers needed for the objects in the Object array
1902      * @throws SerializationException when an object in <code>content</code> cannot be serialized
1903      */
1904     static Serializer<?>[] buildEncoderList(final boolean utf8, final Object... content) throws SerializationException
1905     {
1906         Serializer<?>[] result = new Serializer[content.length];
1907         for (int i = 0; i < content.length; i++)
1908         {
1909             Object object = content[i];
1910             result[i] = findEncoder(utf8, object);
1911         }
1912 
1913         return result;
1914     }
1915 
1916     /**
1917      * Encode the object array into a Big Endian message.
1918      * @param utf8 whether to encode String fields and characters in utf8 or not
1919      * @param endianUtil EndianUtil; encoder for multi-byte values
1920      * @param content the objects to encode
1921      * @return the zeroMQ message to send as a byte array
1922      * @throws SerializationException on unknown data type
1923      */
1924     @SuppressWarnings({"unchecked", "rawtypes"})
1925     private static byte[] encode(final boolean utf8, final EndianUtil endianUtil, final Object... content)
1926             throws SerializationException
1927     {
1928         Serializer[] serializers = buildEncoderList(utf8, content);
1929         // Pass one: compute total size
1930         int size = 0;
1931         for (int i = 0; i < serializers.length; i++)
1932         {
1933             size += serializers[i].sizeWithPrefix(content[i]);
1934         }
1935         // Allocate buffer
1936         byte[] message = new byte[size];
1937         // Pass 2 fill buffer
1938         Pointer pointer = new Pointer();
1939 
1940         for (int i = 0; i < serializers.length; i++)
1941         {
1942             serializers[i].serializeWithPrefix(content[i], message, pointer, endianUtil);
1943             // System.out.println("Expected increment: " + serializers[i].size(content[i]));
1944             // System.out.println(pointer);
1945         }
1946         Throw.when(pointer.get() != message.length, SerializationException.class, "Data size error (reserved %d, used %d)",
1947                 message.length, pointer.get());
1948         return message;
1949     }
1950 
1951     /**
1952      * Code a unit, including MoneyUnits.
1953      * @param unit the unit to code in the byte array
1954      * @param message the byte array
1955      * @param pointer the start pointer in the byte array
1956      * @param endianUtil EndianUtil; encoder to use for multi-byte values
1957      */
1958     @SuppressWarnings("rawtypes")
1959     static void encodeUnit(final Unit unit, final byte[] message, final Pointer pointer, final EndianUtil endianUtil)
1960     {
1961         @SuppressWarnings("unchecked") // TODO see how this can be solved with type <U extends Unit<U>>
1962         SerializationUnits unitType = SerializationUnits.getUnitType(unit);
1963         message[pointer.getAndIncrement(1)] = unitType.getCode();
1964         if (unit instanceof MoneyUnit)
1965         {
1966             @SuppressWarnings("unchecked")
1967             DisplayType displayType = DisplayType.getDisplayType(unit);
1968             endianUtil.encodeShort((short) displayType.getIntCode(), message, pointer.getAndIncrement(2));
1969         }
1970         else if (unit instanceof MoneyPerAreaUnit)
1971         {
1972             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerAreaUnit) unit).getMoneyUnit());
1973             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
1974             DisplayType perType = DisplayType.getDisplayType(((MoneyPerAreaUnit) unit).getAreaUnit());
1975             message[pointer.getAndIncrement(1)] = perType.getByteCode();
1976         }
1977         else if (unit instanceof MoneyPerEnergyUnit)
1978         {
1979             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerEnergyUnit) unit).getMoneyUnit());
1980             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
1981             DisplayType perType = DisplayType.getDisplayType(((MoneyPerEnergyUnit) unit).getEnergyUnit());
1982             message[pointer.getAndIncrement(1)] = perType.getByteCode();
1983         }
1984         else if (unit instanceof MoneyPerLengthUnit)
1985         {
1986             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerLengthUnit) unit).getMoneyUnit());
1987             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
1988             DisplayType perType = DisplayType.getDisplayType(((MoneyPerLengthUnit) unit).getLengthUnit());
1989             message[pointer.getAndIncrement(1)] = perType.getByteCode();
1990         }
1991         else if (unit instanceof MoneyPerMassUnit)
1992         {
1993             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerMassUnit) unit).getMoneyUnit());
1994             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
1995             DisplayType perType = DisplayType.getDisplayType(((MoneyPerMassUnit) unit).getMassUnit());
1996             message[pointer.getAndIncrement(1)] = perType.getByteCode();
1997         }
1998         else if (unit instanceof MoneyPerDurationUnit)
1999         {
2000             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerDurationUnit) unit).getMoneyUnit());
2001             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
2002             DisplayType perType = DisplayType.getDisplayType(((MoneyPerDurationUnit) unit).getDurationUnit());
2003             message[pointer.getAndIncrement(1)] = perType.getByteCode();
2004         }
2005         else if (unit instanceof MoneyPerVolumeUnit)
2006         {
2007             DisplayType moneyType = DisplayType.getDisplayType(((MoneyPerVolumeUnit) unit).getMoneyUnit());
2008             endianUtil.encodeShort((short) moneyType.getIntCode(), message, pointer.getAndIncrement(2));
2009             DisplayType perType = DisplayType.getDisplayType(((MoneyPerVolumeUnit) unit).getVolumeUnit());
2010             message[pointer.getAndIncrement(1)] = perType.getByteCode();
2011         }
2012         else
2013         {
2014             @SuppressWarnings("unchecked")
2015             DisplayType displayType = DisplayType.getDisplayType(unit);
2016             message[pointer.getAndIncrement(1)] = displayType.getByteCode();
2017         }
2018     }
2019 
2020     /**
2021      * Decode the 2-byte Money unit in the message (code 100).
2022      * @param moneyCode Short; the money code
2023      * @return Unit; decoded money unit
2024      */
2025     private static Unit<? extends Unit<?>> decodeMoneyUnit(final Integer moneyCode)
2026     {
2027         DisplayType displayType = DisplayType.getDisplayType(SerializationUnits.MONEY, moneyCode);
2028         return displayType.getDjunitsType();
2029     }
2030 
2031     /**
2032      * Decode the 2-byte MoneyPerUnit unit in the message (code 101 - 106).
2033      * @param unitType the unit type (e.g., MoneyPerArea)
2034      * @param moneyCode Short; the 16-bit money code
2035      * @param perCode Integer; the code for the reciprocal
2036      * @return decoded MoneyPerUnit unit
2037      */
2038     @SuppressWarnings("checkstyle:needbraces")
2039     private static Unit<? extends Unit<?>> decodeMoneyPerUnit(final SerializationUnits unitType, final Integer moneyCode,
2040             final Integer perCode)
2041     {
2042         DisplayType moneyDisplayType = DisplayType.getDisplayType(SerializationUnits.MONEY, moneyCode);
2043         DisplayType perDisplayType;
2044         if (unitType.getCode() == 101)
2045             perDisplayType = DisplayType.getDisplayType(SerializationUnits.AREA, perCode);
2046         else if (unitType.getCode() == 102)
2047             perDisplayType = DisplayType.getDisplayType(SerializationUnits.ENERGY, perCode);
2048         else if (unitType.getCode() == 103)
2049             perDisplayType = DisplayType.getDisplayType(SerializationUnits.LENGTH, perCode);
2050         else if (unitType.getCode() == 104)
2051             perDisplayType = DisplayType.getDisplayType(SerializationUnits.MASS, perCode);
2052         else if (unitType.getCode() == 105)
2053             perDisplayType = DisplayType.getDisplayType(SerializationUnits.DURATION, perCode);
2054         else if (unitType.getCode() == 106)
2055             perDisplayType = DisplayType.getDisplayType(SerializationUnits.VOLUME, perCode);
2056         else
2057             throw new RuntimeException(new SerializationException("Unknown MoneyPerUnit type with code " + unitType.getCode()));
2058         return moneyPerUnitType(moneyDisplayType, perDisplayType);
2059     }
2060 
2061     /** The MoneyPerUnit cache stores the instantiated types so they are not created again and again. */
2062     private static Map<MoneyUnit, Map<Unit<?>, Unit<?>>> moneyPerUnitCache = new HashMap<>();
2063 
2064     /**
2065      * Return the cached or created moneyPerUnitType.
2066      * @param moneyDisplayType the money type to use, e.g. USD
2067      * @param perDisplayType the per-unit to use, e.g. SQUARE_METER
2068      * @return the cached or created moneyPerUnitType
2069      */
2070     public static Unit<?> moneyPerUnitType(final DisplayType moneyDisplayType, final DisplayType perDisplayType)
2071     {
2072         Map<Unit<?>, Unit<?>> moneyMap = moneyPerUnitCache.get(moneyDisplayType.getDjunitsType());
2073         if (moneyMap == null)
2074         {
2075             moneyMap = new HashMap<>();
2076             moneyPerUnitCache.put((MoneyUnit) moneyDisplayType.getDjunitsType(), moneyMap);
2077         }
2078         Unit<?> moneyPerUnitType = moneyMap.get(perDisplayType.getDjunitsType());
2079         if (moneyPerUnitType != null)
2080         {
2081             return moneyPerUnitType;
2082         }
2083         String name = moneyDisplayType.getName() + "/" + perDisplayType.getName();
2084         String abbreviation = moneyDisplayType.getAbbreviation() + "/" + perDisplayType.getAbbreviation();
2085         if (perDisplayType.getUnitType().equals(SerializationUnits.AREA))
2086         {
2087             moneyPerUnitType = new MoneyPerAreaUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2088                     (AreaUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2089         }
2090         else if (perDisplayType.getUnitType().equals(SerializationUnits.ENERGY))
2091         {
2092             moneyPerUnitType = new MoneyPerEnergyUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2093                     (EnergyUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2094         }
2095         else if (perDisplayType.getUnitType().equals(SerializationUnits.LENGTH))
2096         {
2097             moneyPerUnitType = new MoneyPerLengthUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2098                     (LengthUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2099         }
2100         else if (perDisplayType.getUnitType().equals(SerializationUnits.MASS))
2101         {
2102             moneyPerUnitType = new MoneyPerMassUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2103                     (MassUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2104         }
2105         else if (perDisplayType.getUnitType().equals(SerializationUnits.DURATION))
2106         {
2107             moneyPerUnitType = new MoneyPerDurationUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2108                     (DurationUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2109         }
2110         else if (perDisplayType.getUnitType().equals(SerializationUnits.VOLUME))
2111         {
2112             moneyPerUnitType = new MoneyPerVolumeUnit((MoneyUnit) moneyDisplayType.getDjunitsType(),
2113                     (VolumeUnit) perDisplayType.getDjunitsType(), name, abbreviation);
2114         }
2115         else
2116         {
2117             throw new RuntimeException(new SerializationException("Unknown moneyPerUnit type: " + name));
2118         }
2119         moneyMap.put(perDisplayType.getDjunitsType(), moneyPerUnitType);
2120         return moneyPerUnitType;
2121     }
2122 
2123     /**
2124      * Retrieve and decode a DJUNITS unit.
2125      * @param buffer byte[]; the encoded data
2126      * @param pointer Pointer; position in the encoded data where the unit is to be decoded from
2127      * @param endianUtil EndianUtil; decoder for multi-byte values
2128      * @return Unit
2129      */
2130     static Unit<? extends Unit<?>> getUnit(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
2131     {
2132         SerializationUnits unitType = SerializationUnits.getUnitType(buffer[pointer.getAndIncrement(1)]);
2133         if (unitType.getCode() == 100) // money
2134         {
2135             int moneyCode = endianUtil.decodeShort(buffer, pointer.getAndIncrement(2));
2136             return decodeMoneyUnit(moneyCode);
2137         }
2138         else if (unitType.getCode() >= 101 && unitType.getCode() <= 106)
2139         {
2140             int moneyCode = endianUtil.decodeShort(buffer, pointer.getAndIncrement(2));
2141             return decodeMoneyPerUnit(unitType, moneyCode, 0 + buffer[pointer.getAndIncrement(1)]);
2142         }
2143         else
2144         {
2145             DisplayType displayType = DisplayType.getDisplayType(unitType, 0 + buffer[pointer.getAndIncrement(1)]);
2146             return displayType.getDjunitsType();
2147         }
2148     }
2149 
2150     /**
2151      * Decode the message into an object array, constructing Java Primitive data arrays and matrices where possible.
2152      * @param buffer the byte array to decode
2153      * @param endianUtil EndianUtil; decoder for multi-byte values
2154      * @return an array of objects of the right type
2155      * @throws SerializationException on unknown data type
2156      */
2157     public static Object[] decodeToPrimitiveDataTypes(final byte[] buffer, final EndianUtil endianUtil)
2158             throws SerializationException
2159     {
2160         return decode(buffer, PRIMITIVE_DATA_DECODERS, endianUtil);
2161     }
2162 
2163     /**
2164      * Decode the message into an object array, constructing Java Object arrays and matrices where possible.
2165      * @param buffer the byte array to decode
2166      * @param endianUtil EndianUtil; decoder for multi-byte values
2167      * @return an array of objects of the right type
2168      * @throws SerializationException on unknown data type
2169      */
2170     public static Object[] decodeToObjectDataTypes(final byte[] buffer, final EndianUtil endianUtil)
2171             throws SerializationException
2172     {
2173         return decode(buffer, OBJECT_DECODERS, endianUtil);
2174     }
2175 
2176     /**
2177      * Decode the message into an object array.
2178      * @param buffer the byte array to decode
2179      * @param decoderMap Map&lt;Byte, Serializer&lt;?&gt;&gt;; the map with decoders to use
2180      * @param endianUtil EndianUtil; decoder for multi-byte values
2181      * @return an array of objects of the right type
2182      * @throws SerializationException on unknown data type
2183      */
2184     @SuppressWarnings({ "checkstyle:methodlength", "checkstyle:needbraces" })
2185     public static Object[] decode(final byte[] buffer, final Map<Byte, Serializer<?>> decoderMap, final EndianUtil endianUtil)
2186             throws SerializationException
2187     {
2188         List<Object> list = new ArrayList<>();
2189         Pointer pointer = new Pointer();
2190         while (pointer.get() < buffer.length)
2191         {
2192             Byte fieldType = buffer[pointer.getAndIncrement(1)];
2193             Serializer<?> serializer = decoderMap.get(fieldType);
2194             if (null == serializer)
2195             {
2196                 throw new SerializationException("Bad FieldType or no defined decoder for fieldType " + fieldType
2197                         + " at position " + (pointer.get() - 1));
2198             }
2199             else
2200             {
2201                 // System.out.println("Applying deserializer for " + serializer.dataClassName());
2202                 list.add(serializer.deSerialize(buffer, pointer, endianUtil));
2203             }
2204         }
2205         return list.toArray();
2206     }
2207 
2208     /**
2209      * Indicate whether extra bytes are needed for a Money per quantity type.
2210      * @param o the object to check
2211      * @return 1 or 2 to indicate whether an extra byte is needed
2212      */
2213     static int extraBytesMoney(final Object o)
2214     {
2215         if (o instanceof Money)
2216         {
2217             return 1;
2218         }
2219         else if (o instanceof MoneyPerArea || o instanceof MoneyPerEnergy || o instanceof MoneyPerLength
2220                 || o instanceof MoneyPerMass || o instanceof MoneyPerDuration || o instanceof MoneyPerVolume)
2221         {
2222             return 2;
2223         }
2224         else if (o instanceof FloatMoney)
2225         {
2226             return 1;
2227         }
2228         else if (o instanceof FloatMoneyPerArea || o instanceof FloatMoneyPerEnergy || o instanceof FloatMoneyPerLength
2229                 || o instanceof FloatMoneyPerMass || o instanceof FloatMoneyPerDuration || o instanceof FloatMoneyPerVolume)
2230         {
2231             return 2;
2232         }
2233         return 0;
2234     }
2235 
2236     /**
2237      * Minimal implementation of SerializableObject.
2238      */
2239     static class MinimalSerializableObject implements SerializableObject<MinimalSerializableObject>
2240     {
2241         /** The List that is returned by the <code>exportAsList</code> method. */
2242         private final List<Object> list;
2243 
2244         /**
2245          * Construct a new MinimalCompound object.
2246          * @param list List&lt;Object&gt;; the object list that is returned by <code>exportAsList</code> method
2247          */
2248         MinimalSerializableObject(final List<Object> list)
2249         {
2250             this.list = list;
2251         }
2252 
2253         @Override
2254         public List<Object> exportAsList()
2255         {
2256             return this.list;
2257         }
2258 
2259     }
2260 
2261 }