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