View Javadoc
1   package org.djutils.serialization;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertNull;
6   import static org.junit.Assert.assertTrue;
7   import static org.junit.Assert.fail;
8   
9   import java.io.File;
10  import java.io.UnsupportedEncodingException;
11  import java.nio.ByteOrder;
12  import java.util.ArrayList;
13  import java.util.Arrays;
14  import java.util.List;
15  
16  import org.djunits.unit.AccelerationUnit;
17  import org.djunits.unit.AreaUnit;
18  import org.djunits.unit.DimensionlessUnit;
19  import org.djunits.unit.ElectricalCurrentUnit;
20  import org.djunits.unit.ElectricalResistanceUnit;
21  import org.djunits.unit.LengthUnit;
22  import org.djunits.unit.MoneyPerAreaUnit;
23  import org.djunits.unit.MoneyPerDurationUnit;
24  import org.djunits.unit.MoneyPerEnergyUnit;
25  import org.djunits.unit.MoneyPerLengthUnit;
26  import org.djunits.unit.MoneyPerMassUnit;
27  import org.djunits.unit.MoneyPerVolumeUnit;
28  import org.djunits.unit.MoneyUnit;
29  import org.djunits.unit.SpeedUnit;
30  import org.djunits.value.StorageType;
31  import org.djunits.value.ValueException;
32  import org.djunits.value.vdouble.matrix.ElectricalCurrentMatrix;
33  import org.djunits.value.vdouble.scalar.Dimensionless;
34  import org.djunits.value.vdouble.scalar.Length;
35  import org.djunits.value.vdouble.scalar.Money;
36  import org.djunits.value.vdouble.scalar.MoneyPerArea;
37  import org.djunits.value.vdouble.scalar.MoneyPerDuration;
38  import org.djunits.value.vdouble.scalar.MoneyPerEnergy;
39  import org.djunits.value.vdouble.scalar.MoneyPerLength;
40  import org.djunits.value.vdouble.scalar.MoneyPerMass;
41  import org.djunits.value.vdouble.scalar.MoneyPerVolume;
42  import org.djunits.value.vdouble.vector.AbstractDoubleVector;
43  import org.djunits.value.vdouble.vector.ElectricalCurrentVector;
44  import org.djunits.value.vdouble.vector.LengthVector;
45  import org.djunits.value.vdouble.vector.MoneyVector;
46  import org.djunits.value.vfloat.matrix.FloatElectricalResistanceMatrix;
47  import org.djunits.value.vfloat.scalar.FloatArea;
48  import org.djunits.value.vfloat.scalar.FloatMoney;
49  import org.djunits.value.vfloat.scalar.FloatMoneyPerVolume;
50  import org.djunits.value.vfloat.vector.FloatElectricalResistanceVector;
51  import org.djutils.decoderdumper.HexDumper;
52  import org.junit.Test;
53  
54  /**
55   * Test message conversions.
56   * <p>
57   * Copyright (c) 2019-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
58   * BSD-style license. See <a href="https://sim0mq.org/docs/current/license.html">OpenTrafficSim License</a>.
59   * </p>
60   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
61   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
62   */
63  public class Tests
64  {
65  
66      /**
67       * Basic test encoding and decoding of the basic types.
68       * @throws SerializationException when that happens uncaught this test has failed
69       */
70      @Test
71      public void simpleTests() throws SerializationException
72      {
73          int intValue = 123;
74          Integer integerValue = -456;
75          short shortValue = 234;
76          Short shortValue2 = -345;
77          long longValue = 98765L;
78          Long longValue2 = -98765L;
79          Byte byteValue = 12;
80          byte byteValue2 = -23;
81          float floatValue = 1.234f;
82          Float floatValue2 = -3.456f;
83          double doubleValue = 4.56789;
84          Double doubleValue2 = -4.56789;
85          boolean boolValue = true;
86          Boolean boolValue2 = false;
87          Character charValue = 'a';
88          char charValue2 = 'b';
89          String stringValue = "abcDEF123!@#ȦȧȨ\u0776\u0806\u080e";
90          Object[] objects = new Object[] {intValue, integerValue, shortValue, shortValue2, longValue, longValue2, byteValue,
91                  byteValue2, floatValue, floatValue2, doubleValue, doubleValue2, boolValue, boolValue2, charValue, charValue2,
92                  stringValue};
93          for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
94          {
95              for (boolean encodeUTF8 : new boolean[] {false, true})
96              {
97                  // System.out.println("" + endianUtil + ", UTF8=" + encodeUTF8);
98                  byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
99                          : TypedMessage.encodeUTF16(endianUtil, objects);
100                 System.out.print(HexDumper.hexDumper(serialized));
101                 System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
102                 for (boolean primitive : new boolean[] {false, true})
103                 {
104                     Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
105                             : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
106                     assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
107                     for (int i = 0; i < objects.length; i++)
108                     {
109                         assertEquals(
110                                 "decoded object at index " + i + "(" + objects[i] + ") equals corresponding object in input",
111                                 objects[i], decodedObjects[i]);
112                     }
113                 }
114             }
115         }
116     }
117 
118     /**
119      * Test encoding and decoding of Strings with more exotic characters for UTF-8 and UTF-16.
120      * @throws SerializationException when that happens uncaught this test has failed
121      * @throws UnsupportedEncodingException when UTF-8 en/decoding fails
122      */
123     @Test
124     public void testStrings() throws SerializationException, UnsupportedEncodingException
125     {
126         String abc = "abc";
127         String copyright = "" + '\u00A9';
128         String xi = "" + '\u03BE';
129         String permille = "" + '\u2030';
130         String smiley = "\uD83D\uDE00";
131         String complex = smiley + copyright + xi + permille;
132 
133         testString(3, 6, abc);
134         testString(2, 2, copyright);
135         testString(3, 2, permille);
136         testString(2, 2, xi);
137         testString(4, 4, smiley);
138 
139         compare(TypedMessage.encodeUTF8(EndianUtil.BIG_ENDIAN, permille),
140                 new byte[] {9, 0, 0, 0, 3, (byte) 0xE2, (byte) 0x80, (byte) 0xB0});
141         compare(TypedMessage.encodeUTF16(EndianUtil.BIG_ENDIAN, permille),
142                 new byte[] {10, 0, 0, 0, 1, (byte) 0x20, (byte) 0x30});
143 
144         compare(TypedMessage.encodeUTF8(EndianUtil.BIG_ENDIAN, smiley),
145                 new byte[] {9, 0, 0, 0, 4, (byte) 0xF0, (byte) 0x9F, (byte) 0x98, (byte) 0x80});
146         compare(TypedMessage.encodeUTF16(EndianUtil.BIG_ENDIAN, smiley),
147                 new byte[] {10, 0, 0, 0, 2, (byte) 0xD8, (byte) 0x3D, (byte) 0xDE, (byte) 0x00});
148 
149         Object[] objects = new Object[] {copyright, xi, permille, smiley, abc, complex};
150         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
151         {
152             for (boolean encodeUTF8 : new boolean[] {false, true})
153             {
154                 byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
155                         : TypedMessage.encodeUTF16(endianUtil, objects);
156                 System.out.print(HexDumper.hexDumper(serialized));
157                 System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
158                 Object[] decodedObjects = TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
159                 assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
160                 for (int i = 0; i < objects.length; i++)
161                 {
162                     assertEquals("decoded object at index " + i + "(" + objects[i] + ") equals corresponding object in input",
163                             objects[i], decodedObjects[i]);
164                 }
165             }
166         }
167 
168     }
169 
170     /**
171      * Compare two byte arrays.
172      * @param actual the calculated byte array
173      * @param expected the expected byte array
174      */
175     private void compare(final byte[] actual, final byte[] expected)
176     {
177         assertEquals(expected.length, actual.length);
178         for (int i = 0; i < expected.length; i++)
179         {
180             assertEquals("byte " + i + " expected: " + expected[i] + ", actual: " + actual[i], expected[i], actual[i]);
181         }
182     }
183 
184     /**
185      * Test encoding and decoding of one String for UTF-8 and UTF-16.
186      * @param expected8 expected length of UTF-8 encoding
187      * @param expected16 expected length of UTF-16 encoding
188      * @param s the string to test
189      * @throws SerializationException when that happens uncaught this test has failed
190      * @throws UnsupportedEncodingException when UTF-8 en/decoding fails
191      */
192     private void testString(final int expected8, final int expected16, final String s)
193             throws SerializationException, UnsupportedEncodingException
194     {
195         assertEquals(expected8, s.getBytes("UTF-8").length);
196         assertEquals(expected16, s.getBytes("UTF-16BE").length);
197         assertEquals(expected16, s.getBytes("UTF-16LE").length);
198 
199         byte[] b8 = TypedMessage.encodeUTF8(EndianUtil.BIG_ENDIAN, s);
200         byte[] b16BE = TypedMessage.encodeUTF16(EndianUtil.BIG_ENDIAN, s);
201         byte[] b16LE = TypedMessage.encodeUTF16(EndianUtil.LITTLE_ENDIAN, s);
202 
203         assertEquals(expected8, b8.length - 5);
204         assertEquals(expected16, b16BE.length - 5);
205         assertEquals(expected16, b16LE.length - 5);
206 
207         // get the number from the byte arrays
208         assertEquals(9, b8[0]);
209         assertEquals(expected8, EndianUtil.BIG_ENDIAN.decodeInt(b8, 1));
210         assertEquals(10, b16BE[0]);
211         assertEquals(expected16 / 2, EndianUtil.BIG_ENDIAN.decodeInt(b16BE, 1));
212         // TODO: assertEquals(10, b16LE[0]); the code for UTF-16LE will be different from 10 in a next version of djutils
213         assertEquals(expected16 / 2, EndianUtil.LITTLE_ENDIAN.decodeInt(b16LE, 1));
214     }
215 
216     /**
217      * Test encoding and decoding of arrays.
218      * @throws SerializationException when that happens uncaught this test has failed
219      */
220     @Test
221     public void testArrays() throws SerializationException
222     {
223         int[] integer = new int[] {1, 2, 3};
224         Integer[] integerValues2 = new Integer[] {-1, -2, -3};
225         short[] shortValues = new short[] {10, 20, 30};
226         Short[] shortValues2 = new Short[] {-10, -20, -30};
227         long[] longValues = new long[] {1000, 2000, 3000};
228         Long[] longValues2 = new Long[] {-1000L, -2000L, -3000L};
229         byte[] byteValues = new byte[] {12, 13, 14};
230         Byte[] byteValues2 = new Byte[] {-12, -13, -14};
231         boolean[] boolValues = new boolean[] {false, true, true};
232         Boolean[] boolValues2 = new Boolean[] {true, true, false};
233         float[] floatValues = new float[] {12.3f, 23.4f, 34.5f};
234         Float[] floatValues2 = new Float[] {-12.3f, -23.4f, -34.5f};
235         double[] doubleValues = new double[] {23.45, 34.56, 45.67};
236         Double[] doubleValues2 = new Double[] {-23.45, -34.56, -45.67};
237         Object[] objects = new Object[] {integer, integerValues2, shortValues, shortValues2, longValues, longValues2,
238                 byteValues, byteValues2, floatValues, floatValues2, doubleValues, doubleValues2, boolValues, boolValues2};
239         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
240         {
241             for (boolean encodeUTF8 : new boolean[] {false, true})
242             {
243                 byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
244                         : TypedMessage.encodeUTF16(endianUtil, objects);
245                 System.out.print(HexDumper.hexDumper(serialized));
246                 System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
247                 for (boolean primitive : new boolean[] {false, true})
248                 {
249                     Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
250                             : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
251                     assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
252                     for (int i = 0; i < objects.length; i++)
253                     {
254                         assertTrue("decoded object at index " + i + "(" + objects[i] + ") equals corresponding object in input",
255                                 deepEquals0(makePrimitive(objects[i]), makePrimitive(decodedObjects[i])));
256                     }
257                 }
258             }
259         }
260     }
261 
262     /**
263      * Test encoding and decoding of arrays.
264      * @throws SerializationException when that happens uncaught this test has failed
265      */
266     @Test
267     public void testMatrices() throws SerializationException
268     {
269         int[][] integer = new int[][] {{1, 2, 3}, {4, 5, 6}};
270         Integer[][] integerValues2 = new Integer[][] {{-1, -2, -3}, {-4, -5, -6}};
271         short[][] shortValues = new short[][] {{10, 20, 30}, {40, 50, 60}};
272         Short[][] shortValues2 = new Short[][] {{-10, -20, -30}, {-40, -50, -60}};
273         long[][] longValues = new long[][] {{1000, 2000, 3000}, {3000, 4000, 5000}};
274         Long[][] longValues2 = new Long[][] {{-1000L, -2000L, -3000L}, {-3000L, -4000L, -5000L}};
275         byte[][] byteValues = new byte[][] {{12, 13, 14}, {15, 16, 17}};
276         Byte[][] byteValues2 = new Byte[][] {{-12, -13, -14}, {-15, -16, -17}};
277         boolean[][] boolValues = new boolean[][] {{false, true, true}, {false, false, false}};
278         Boolean[][] boolValues2 = new Boolean[][] {{true, true, false}, {true, true, true}};
279         float[][] floatValues = new float[][] {{12.3f, 23.4f, 34.5f}, {44.4f, 55.5f, 66.6f}};
280         Float[][] floatValues2 = new Float[][] {{-12.3f, -23.4f, -34.5f}, {-11.1f, -22.2f, -33.3f}};
281         double[][] doubleValues = new double[][] {{23.45, 34.56, 45.67}, {55.5, 66.6, 77.7}};
282         Double[][] doubleValues2 = new Double[][] {{-23.45, -34.56, -45.67}, {-22.2, -33.3, -44.4}};
283         Object[] objects = new Object[] {integer, integerValues2, shortValues, shortValues2, longValues, longValues2,
284                 byteValues, byteValues2, floatValues, floatValues2, doubleValues, doubleValues2, boolValues, boolValues2};
285         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
286         {
287             for (boolean encodeUTF8 : new boolean[] {false, true})
288             {
289                 byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
290                         : TypedMessage.encodeUTF16(endianUtil, objects);
291                 System.out.print(HexDumper.hexDumper(serialized));
292                 System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
293                 for (boolean primitive : new boolean[] {false, true})
294                 {
295                     Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
296                             : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
297                     assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
298                     for (int i = 0; i < objects.length; i++)
299                     {
300                         assertTrue("decoded object at index " + i + "(" + objects[i] + ") equals corresponding object in input",
301                                 deepEquals0(makePrimitive(objects[i]), makePrimitive(decodedObjects[i])));
302                     }
303                 }
304             }
305         }
306     }
307 
308     /**
309      * Test encoding and decoding of strongly typed quantities (DJUNITS).
310      * @throws SerializationException when that happens uncaught, this test has failed
311      * @throws ValueException when that happens uncaught, this test has failed
312      */
313     @Test
314     public void testDJunits() throws SerializationException, ValueException
315     {
316         Length length = new Length(123.4, LengthUnit.FOOT);
317         Dimensionless value = new Dimensionless(345.6, DimensionlessUnit.SI);
318         Money money = new Money(456, MoneyUnit.EUR);
319         FloatMoney floatMoney = new FloatMoney(123.45f, MoneyUnit.AED);
320         MoneyPerArea mpa = new MoneyPerArea(0.33, MoneyPerAreaUnit.USD_PER_ACRE);
321         MoneyPerLength mpl = new MoneyPerLength(0.22, MoneyPerLengthUnit.USD_PER_MILE);
322         MoneyPerEnergy mpe = new MoneyPerEnergy(0.33, MoneyPerEnergyUnit.EUR_PER_KILOWATTHOUR);
323         MoneyPerMass mpm = new MoneyPerMass(0.33, MoneyPerMassUnit.USD_PER_POUND);
324         MoneyPerDuration mpt = new MoneyPerDuration(0.33, MoneyPerDurationUnit.EUR_PER_DAY);
325         MoneyPerVolume mpv = new MoneyPerVolume(0.44, MoneyPerVolumeUnit.USD_PER_OUNCE_US_FLUID);
326         FloatMoneyPerVolume mpvw = new FloatMoneyPerVolume(0.55, MoneyPerVolumeUnit.USD_PER_OUNCE_US_FLUID);
327         FloatArea area = new FloatArea(66.66f, AreaUnit.ACRE);
328         ElectricalCurrentVector currents =
329                 new ElectricalCurrentVector(new double[] {1.2, 2.3, 3.4}, ElectricalCurrentUnit.MILLIAMPERE, StorageType.DENSE);
330         FloatElectricalResistanceVector resistors = new FloatElectricalResistanceVector(new float[] {1.2f, 4.7f, 6.8f},
331                 ElectricalResistanceUnit.KILOOHM, StorageType.DENSE);
332         ElectricalCurrentMatrix currentMatrix = new ElectricalCurrentMatrix(new double[][] {{1.2, 2.3, 3.4}, {5.5, 6.6, 7.7}},
333                 ElectricalCurrentUnit.MILLIAMPERE, StorageType.DENSE);
334         FloatElectricalResistanceMatrix resistorMatrix = new FloatElectricalResistanceMatrix(
335                 new float[][] {{1.2f, 4.7f, 6.8f}, {2.2f, 3.3f, 4.4f}}, ElectricalResistanceUnit.KILOOHM, StorageType.DENSE);
336 
337         Object[] objects = new Object[] {length, value, money, floatMoney, mpa, mpl, mpe, mpm, mpt, mpv, mpvw, area, currents,
338                 resistors, currentMatrix, resistorMatrix};
339         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
340         {
341             byte[] serialized = TypedMessage.encodeUTF16(endianUtil, objects);
342             System.out.print(HexDumper.hexDumper(serialized));
343             System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
344             for (boolean primitive : new boolean[] {false, true})
345             {
346                 Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
347                         : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
348                 assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
349                 for (int i = 0; i < objects.length; i++)
350                 {
351                     assertTrue("decoded object at index " + i + "(" + objects[i] + ") equals corresponding object in input",
352                             deepEquals0(makePrimitive(objects[i]), makePrimitive(decodedObjects[i])));
353                 }
354             }
355         }
356     }
357 
358     /** Class used to test serialization of classes that implement SerializableObject. */
359     static class Compound implements SerializableObject<Compound>
360     {
361         /** Field 1. */
362         public Integer intValue;
363 
364         /** Field 2. */
365         public Double doubleValue;
366 
367         @Override
368         public int hashCode()
369         {
370             final int prime = 31;
371             int result = 1;
372             result = prime * result + ((this.doubleValue == null) ? 0 : this.doubleValue.hashCode());
373             result = prime * result + ((this.intValue == null) ? 0 : this.intValue.hashCode());
374             return result;
375         }
376 
377         @SuppressWarnings("checkstyle:needbraces")
378         @Override
379         public boolean equals(final Object obj)
380         {
381             if (this == obj)
382                 return true;
383             if (obj == null)
384                 return false;
385             if (getClass() != obj.getClass())
386                 return false;
387             Compound other = (Compound) obj;
388             if (this.doubleValue == null)
389             {
390                 if (other.doubleValue != null)
391                     return false;
392             }
393             else if (!this.doubleValue.equals(other.doubleValue))
394                 return false;
395             if (this.intValue == null)
396             {
397                 if (other.intValue != null)
398                     return false;
399             }
400             else if (!this.intValue.equals(other.intValue))
401                 return false;
402             return true;
403         }
404 
405         @Override
406         public String toString()
407         {
408             return "Compound [intValue=" + this.intValue + ", doubleValue=" + this.doubleValue + "]";
409         }
410 
411         /**
412          * Construct a new Compound object.
413          * @param intValue int; the value to assign to intValue
414          * @param doubleValue double; the value to assign to doubleValue
415          */
416         Compound(final int intValue, final double doubleValue)
417         {
418             this.intValue = intValue;
419             this.doubleValue = doubleValue;
420         }
421 
422         @Override
423         public List<Object> exportAsList()
424         {
425             List<Object> result = new ArrayList<>();
426             result.add(this.intValue);
427             result.add(this.doubleValue);
428             return result;
429         }
430 
431     }
432 
433     /**
434      * Test the compound array encoder and decoder.
435      * @throws SerializationException when that happens uncaught, this test has failed
436      */
437     @Test
438     public void testCompoundArrays() throws SerializationException
439     {
440         Compound[] testArray = new Compound[] {new Compound(1, 0.1), new Compound(2, 0.2), new Compound(3, 0.3)};
441         Object[] objects = new Object[] {testArray};
442         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
443         {
444             for (boolean encodeUTF8 : new boolean[] {false, true})
445             {
446                 // System.out.println("Encoding " + (encodeUTF8 ? "UTF8" : "UTF16") + ", " + endianUtil);
447                 byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
448                         : TypedMessage.encodeUTF16(endianUtil, objects);
449                 // System.out.print(HexDumper.hexDumper(serialized));
450                 for (boolean primitive : new boolean[] {false, true})
451                 {
452                     Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
453                             : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
454                     assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
455                     // Replace all List objects in the result by corresponding new Compound objects
456                     for (int i = 0; i < objects.length; i++)
457                     {
458                         Object o = decodedObjects[i];
459                         if (o instanceof TypedMessage.MinimalSerializableObject[])
460                         {
461                             TypedMessage.MinimalSerializableObject[] in = ((TypedMessage.MinimalSerializableObject[]) o);
462                             Compound[] out = new Compound[in.length];
463                             for (int j = 0; j < in.length; j++)
464                             {
465                                 List<Object> fields = in[j].exportAsList();
466                                 Integer intValue = (Integer) fields.get(0);
467                                 Double doubleValue = (Double) fields.get(1);
468                                 out[j] = new Compound(intValue, doubleValue);
469                             }
470                             decodedObjects[i] = out;
471                         }
472                     }
473                     for (int i = 0; i < objects.length; i++)
474                     {
475                         if (objects[i] instanceof Compound[])
476                         {
477                             Compound[] in = (Compound[]) objects[i];
478                             assertTrue("decoded object is now also a Compound[]", decodedObjects[i] instanceof Compound[]);
479                             Compound[] out = (Compound[]) objects[i];
480                             assertEquals("Compound arrays have same length", in.length, out.length);
481                             for (int j = 0; j < in.length; j++)
482                             {
483                                 assertEquals("reconstructed compound object matches input", in[j], out[j]);
484                             }
485                         }
486                         else
487                         {
488                             assertTrue(
489                                     "decoded object at index " + i + "(" + objects[i]
490                                             + ") equals corresponding object in input",
491                                     deepEquals0(makePrimitive(objects[i]), makePrimitive(decodedObjects[i])));
492                         }
493                     }
494                 }
495             }
496         }
497     }
498 
499     /**
500      * Test serialization and deserialization of arrays of Djutils vectors.
501      * @throws ValueException if that happens uncaught; this test has failed
502      * @throws SerializationException if that happens uncaught; this test has failed
503      */
504     @Test
505     public void testArrayOfDjutilsVectors() throws ValueException, SerializationException
506     {
507         AbstractDoubleVector<?, ?>[] array =
508                 new AbstractDoubleVector[] {new LengthVector(new double[] {0.1, 0.2, 0.3}, LengthUnit.INCH, StorageType.DENSE),
509                         new MoneyVector(new double[] {10.1, 20.2, 30.3}, MoneyUnit.EUR, StorageType.DENSE)};
510         Object[] objects = new Object[] {array};
511         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
512         {
513             for (boolean encodeUTF8 : new boolean[] {false, true})
514             {
515                 // System.out.println("Encoding " + (encodeUTF8 ? "UTF8" : "UTF16") + ", " + endianUtil);
516                 byte[] serialized = encodeUTF8 ? TypedMessage.encodeUTF8(endianUtil, objects)
517                         : TypedMessage.encodeUTF16(endianUtil, objects);
518                 System.out.print(HexDumper.hexDumper(serialized));
519                 System.out.print(SerialDataDumper.serialDataDumper(endianUtil, serialized));
520                 for (boolean primitive : new boolean[] {false, true})
521                 {
522                     Object[] decodedObjects = primitive ? TypedMessage.decodeToPrimitiveDataTypes(serialized, endianUtil)
523                             : TypedMessage.decodeToObjectDataTypes(serialized, endianUtil);
524                     assertEquals("Size of decoded matches", objects.length, decodedObjects.length);
525                     for (int i = 0; i < objects.length; i++)
526                     {
527                         if (objects[i] instanceof AbstractDoubleVector<?, ?>[])
528                         {
529                             AbstractDoubleVector<?, ?>[] arrayIn = (AbstractDoubleVector<?, ?>[]) objects[i];
530                             AbstractDoubleVector<?, ?>[] arrayOut = (AbstractDoubleVector<?, ?>[]) decodedObjects[i];
531                             for (int j = 0; j < arrayOut.length; j++)
532                             {
533                                 assertEquals("Decoded Djutils array vector element matches", arrayIn[j], arrayOut[j]);
534                             }
535                         }
536                         else
537                         {
538                             assertTrue(
539                                     "decoded object at index " + i + "(" + objects[i]
540                                             + ") equals corresponding object in input",
541                                     deepEquals0(makePrimitive(objects[i]), makePrimitive(decodedObjects[i])));
542                         }
543                     }
544                 }
545             }
546         }
547     }
548 
549     /**
550      * Test that jagged matrices are detected and cause a SerializationException.
551      */
552     @Test
553     public void testJaggedMatrices()
554     {
555         int[][] integer = new int[][] {{1, 2, 3}, {5, 6}};
556         Integer[][] integerValues2 = new Integer[][] {{-1, -2}, {-4, -5, -6}};
557         short[][] shortValues = new short[][] {{10, 20}, {40, 50, 60}};
558         Short[][] shortValues2 = new Short[][] {{-10, -20, -30}, {-40, -50}};
559         long[][] longValues = new long[][] {{1000, 2000, 3000}, {3000, 4000}};
560         Long[][] longValues2 = new Long[][] {{-1000L, -2000L}, {-3000L, -4000L, -5000L}};
561         byte[][] byteValues = new byte[][] {{12, 13}, {15, 16, 17}};
562         Byte[][] byteValues2 = new Byte[][] {{-12, -13, -14}, {-15, -16}};
563         boolean[][] boolValues = new boolean[][] {{false, true, true}, {false, false}};
564         Boolean[][] boolValues2 = new Boolean[][] {{true, true}, {true, true, true}};
565         float[][] floatValues = new float[][] {{12.3f, 23.4f}, {44.4f, 55.5f, 66.6f}};
566         Float[][] floatValues2 = new Float[][] {{-12.3f, -23.4f, -34.5f}, {-11.1f, -22.2f}};
567         double[][] doubleValues = new double[][] {{23.45, 34.56, 45.67}, {55.5, 66.6}};
568         Double[][] doubleValues2 = new Double[][] {{-23.45, -34.56}, {-22.2, -33.3, -44.4}};
569         Object[] objects = new Object[] {integer, integerValues2, shortValues, shortValues2, longValues, longValues2,
570                 byteValues, byteValues2, floatValues, floatValues2, doubleValues, doubleValues2, boolValues, boolValues2};
571         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
572         {
573             for (Object object : objects)
574             {
575                 Object[] singleObjectArray = new Object[] {object};
576                 try
577                 {
578                     TypedMessage.encodeUTF16(endianUtil, singleObjectArray);
579                     fail("Jagged array should have thrown a SerializationException");
580                 }
581                 catch (SerializationException se)
582                 {
583                     // Ignore expected exception
584                 }
585                 try
586                 {
587                     TypedMessage.encodeUTF8(endianUtil, singleObjectArray);
588                     fail("Jagged array should have thrown a SerializationException");
589                 }
590                 catch (SerializationException se)
591                 {
592                     // Ignore expected exception
593                 }
594             }
595         }
596     }
597 
598     /**
599      * Test that the encoder throws a SerializationException when given something that it does not know how to serialize.
600      */
601     @Test
602     public void testUnhandledObject()
603     {
604         File file = new File("whatever");
605         Object[] objects = new Object[] {file};
606         for (EndianUtil endianUtil : new EndianUtil[] {EndianUtil.BIG_ENDIAN, EndianUtil.LITTLE_ENDIAN})
607         {
608             try
609             {
610                 TypedMessage.encodeUTF16(endianUtil, objects);
611                 fail("Non serializable object should have thrown a SerializationException");
612             }
613             catch (SerializationException se)
614             {
615                 // Ignore expected exception
616             }
617 
618             Integer[][] badMatrix = new Integer[0][0];
619             objects = new Object[] {badMatrix};
620             try
621             {
622                 TypedMessage.encodeUTF16(endianUtil, objects);
623                 fail("Zero sized matrix should have thrown a SerializationException");
624             }
625             catch (SerializationException se)
626             {
627                 // Ignore expected exception
628             }
629         }
630     }
631 
632     /**
633      * Test the Pointer class.
634      */
635     @Test
636     public void pointerTest()
637     {
638         Pointer pointer = new Pointer();
639         assertEquals("initial offset is 0", 0, pointer.get());
640         assertEquals("initial offset is 0", 0, pointer.getAndIncrement(10));
641         assertEquals("offset is now 10", 10, pointer.get());
642         pointer.inc(20);
643         assertEquals("offset is now 30", 30, pointer.get());
644         assertTrue("ToString method returns something descriptive", pointer.toString().startsWith("Pointer"));
645     }
646 
647     /**
648      * Convert an array, or matrix of Byte, Short, Integer, etc. to an array/matrix of byte, short, int, etc.
649      * @param in Object; the array to convert
650      * @return Object; the converted input (if conversion was possible), or the unconverted input.
651      */
652     static Object makePrimitive(final Object in)
653     {
654         if (in instanceof Byte[])
655         {
656             Byte[] byteIn = (Byte[]) in;
657             byte[] result = new byte[byteIn.length];
658             for (int i = 0; i < result.length; i++)
659             {
660                 result[i] = byteIn[i];
661             }
662             return result;
663         }
664         if (in instanceof Short[])
665         {
666             Short[] shortIn = (Short[]) in;
667             short[] result = new short[shortIn.length];
668             for (int i = 0; i < result.length; i++)
669             {
670                 result[i] = shortIn[i];
671             }
672             return result;
673         }
674         if (in instanceof Integer[])
675         {
676             Integer[] integerIn = (Integer[]) in;
677             int[] result = new int[integerIn.length];
678             for (int i = 0; i < result.length; i++)
679             {
680                 result[i] = integerIn[i];
681             }
682             return result;
683         }
684         if (in instanceof Long[])
685         {
686             Long[] longIn = (Long[]) in;
687             long[] result = new long[longIn.length];
688             for (int i = 0; i < result.length; i++)
689             {
690                 result[i] = longIn[i];
691             }
692             return result;
693         }
694         if (in instanceof Float[])
695         {
696             Float[] floatIn = (Float[]) in;
697             float[] result = new float[floatIn.length];
698             for (int i = 0; i < result.length; i++)
699             {
700                 result[i] = floatIn[i];
701             }
702             return result;
703         }
704         if (in instanceof Double[])
705         {
706             Double[] doubleIn = (Double[]) in;
707             double[] result = new double[doubleIn.length];
708             for (int i = 0; i < result.length; i++)
709             {
710                 result[i] = doubleIn[i];
711             }
712             return result;
713         }
714         if (in instanceof Boolean[])
715         {
716             Boolean[] booleanIn = (Boolean[]) in;
717             boolean[] result = new boolean[booleanIn.length];
718             for (int i = 0; i < result.length; i++)
719             {
720                 result[i] = booleanIn[i];
721             }
722             return result;
723         }
724         if (in instanceof Byte[][])
725         {
726             Byte[][] byteIn = (Byte[][]) in;
727             byte[][] result = new byte[byteIn.length][byteIn[0].length];
728             for (int i = 0; i < result.length; i++)
729             {
730                 for (int j = 0; j < result[0].length; j++)
731                 {
732                     result[i][j] = byteIn[i][j];
733                 }
734             }
735             return result;
736         }
737         if (in instanceof Short[][])
738         {
739             Short[][] shortIn = (Short[][]) in;
740             short[][] result = new short[shortIn.length][shortIn[0].length];
741             for (int i = 0; i < result.length; i++)
742             {
743                 for (int j = 0; j < result[0].length; j++)
744                 {
745                     result[i][j] = shortIn[i][j];
746                 }
747             }
748             return result;
749         }
750         if (in instanceof Integer[][])
751         {
752             Integer[][] integerIn = (Integer[][]) in;
753             int[][] result = new int[integerIn.length][integerIn[0].length];
754             for (int i = 0; i < result.length; i++)
755             {
756                 for (int j = 0; j < result[0].length; j++)
757                 {
758                     result[i][j] = integerIn[i][j];
759                 }
760             }
761             return result;
762         }
763         if (in instanceof Long[][])
764         {
765             Long[][] longIn = (Long[][]) in;
766             long[][] result = new long[longIn.length][longIn[0].length];
767             for (int i = 0; i < result.length; i++)
768             {
769                 for (int j = 0; j < result[0].length; j++)
770                 {
771                     result[i][j] = longIn[i][j];
772                 }
773             }
774             return result;
775         }
776         if (in instanceof Float[][])
777         {
778             Float[][] floatIn = (Float[][]) in;
779             float[][] result = new float[floatIn.length][floatIn[0].length];
780             for (int i = 0; i < result.length; i++)
781             {
782                 for (int j = 0; j < result[0].length; j++)
783                 {
784                     result[i][j] = floatIn[i][j];
785                 }
786             }
787             return result;
788         }
789         if (in instanceof Double[][])
790         {
791             Double[][] doubleIn = (Double[][]) in;
792             double[][] result = new double[doubleIn.length][doubleIn[0].length];
793             for (int i = 0; i < result.length; i++)
794             {
795                 for (int j = 0; j < result[0].length; j++)
796                 {
797                     result[i][j] = doubleIn[i][j];
798                 }
799             }
800             return result;
801         }
802         if (in instanceof Boolean[][])
803         {
804             Boolean[][] booleanIn = (Boolean[][]) in;
805             boolean[][] result = new boolean[booleanIn.length][booleanIn[0].length];
806             for (int i = 0; i < result.length; i++)
807             {
808                 for (int j = 0; j < result[0].length; j++)
809                 {
810                     result[i][j] = booleanIn[i][j];
811                 }
812             }
813             return result;
814         }
815         return in;
816     }
817 
818     /**
819      * Compare two arrays of any type (stolen from java.util.Arrays).
820      * @param e1 Object (should be some kind of array)
821      * @param e2 Object (should be some kind of array)
822      * @return boolean; true of the arrays have the same type, size and all elements in the arrays are equal to their
823      *         counterpart
824      */
825     static boolean deepEquals0(final Object e1, final Object e2)
826     {
827         if (e1 instanceof Object[] && e2 instanceof Object[])
828         {
829             return Arrays.deepEquals((Object[]) e1, (Object[]) e2);
830         }
831         if (e1 instanceof byte[] && e2 instanceof byte[])
832         {
833             return Arrays.equals((byte[]) e1, (byte[]) e2);
834         }
835         if (e1 instanceof short[] && e2 instanceof short[])
836         {
837             return Arrays.equals((short[]) e1, (short[]) e2);
838         }
839         if (e1 instanceof int[] && e2 instanceof int[])
840         {
841             return Arrays.equals((int[]) e1, (int[]) e2);
842         }
843         if (e1 instanceof long[] && e2 instanceof long[])
844         {
845             return Arrays.equals((long[]) e1, (long[]) e2);
846         }
847         if (e1 instanceof char[] && e2 instanceof char[])
848         {
849             return Arrays.equals((char[]) e1, (char[]) e2);
850         }
851         if (e1 instanceof float[] && e2 instanceof float[])
852         {
853             return Arrays.equals((float[]) e1, (float[]) e2);
854         }
855         if (e1 instanceof double[] && e2 instanceof double[])
856         {
857             return Arrays.equals((double[]) e1, (double[]) e2);
858         }
859         if (e1 instanceof boolean[] && e2 instanceof boolean[])
860         {
861             return Arrays.equals((boolean[]) e1, (boolean[]) e2);
862         }
863         return e1.equals(e2);
864     }
865 
866     /**
867      * Test the UnitType class.
868      */
869     @Test
870     public void testUnitType()
871     {
872         byte code = 127;
873         Class<AccelerationUnit> unitClass = AccelerationUnit.class;
874         String name = "AccelerationName";
875         String description = "AccelerationDescription";
876         String siUnit = "[m/s^2]";
877         SerializationUnits testAccelerationUnitType = new SerializationUnits(code, unitClass, name, description, siUnit);
878         assertEquals("code is returned", code, testAccelerationUnitType.getCode());
879         assertEquals("unit class is returned", unitClass, testAccelerationUnitType.getDjunitsType());
880         assertEquals("name is returned", name, testAccelerationUnitType.getName());
881         assertEquals("description is returned", description, testAccelerationUnitType.getDescription());
882         assertEquals("SI unit is returned", siUnit, testAccelerationUnitType.getSiUnit());
883         assertTrue("toString returns something descriptive", testAccelerationUnitType.toString().startsWith("UnitType"));
884 
885         byte undefined = 126;
886         assertEquals("new unit is in the byte type map", testAccelerationUnitType, SerializationUnits.getUnitType(code));
887         assertNull("undefined byte returns null", SerializationUnits.getUnitType(undefined));
888         assertEquals("djunits type is returned", unitClass, SerializationUnits.getUnitClass(code));
889         assertNull("undefined byte returns null", SerializationUnits.getUnitClass(undefined));
890         assertEquals("speed type can be found by byte code", SerializationUnits.SPEED,
891                 SerializationUnits.getUnitType((byte) 22));
892         assertEquals("speed type can be found by unit type", SerializationUnits.SPEED,
893                 SerializationUnits.getUnitType(SpeedUnit.SI));
894         assertEquals("speed type can be found by non SI unit type", SerializationUnits.SPEED,
895                 SerializationUnits.getUnitType(SpeedUnit.FOOT_PER_SECOND));
896         assertEquals("speed unit code can be found by unit type", 22, SerializationUnits.getUnitCode(SpeedUnit.SI));
897     }
898 
899     /**
900      * Test all constructors for SerializationException.
901      */
902     @Test
903     public final void serializationExceptionTest()
904     {
905         String message = "MessageString";
906         Exception e = new SerializationException(message);
907         assertEquals("message should be our message", message, e.getMessage());
908         assertEquals("cause should be null", null, e.getCause());
909         e = new SerializationException();
910         assertEquals("cause should be null", null, e.getCause());
911         String causeString = "CauseString";
912         Throwable cause = new Throwable(causeString);
913         e = new SerializationException(cause);
914         assertEquals("cause should not be our cause", cause, e.getCause());
915         assertEquals("cause description should be our cause string", causeString, e.getCause().getMessage());
916         e = new SerializationException(message, cause);
917         assertEquals("message should be our message", message, e.getMessage());
918         assertEquals("cause should not be our cause", cause, e.getCause());
919         assertEquals("cause description should be our cause string", causeString, e.getCause().getMessage());
920         for (boolean enableSuppression : new boolean[] {true, false})
921         {
922             for (boolean writableStackTrace : new boolean[] {true, false})
923             {
924                 e = new SerializationException(message, cause, enableSuppression, writableStackTrace);
925                 assertTrue("Exception should not be null", null != e);
926                 assertEquals("message should be our message", message, e.getMessage());
927                 assertEquals("cause should not be our cause", cause, e.getCause());
928                 assertEquals("cause description should be our cause string", causeString, e.getCause().getMessage());
929                 // Don't know how to check if suppression is enabled/disabled
930                 StackTraceElement[] stackTrace = new StackTraceElement[1];
931                 stackTrace[0] = new StackTraceElement("a", "b", "c", 1234);
932                 try
933                 {
934                     e.setStackTrace(stackTrace);
935                 }
936                 catch (Exception e1)
937                 {
938                     assertTrue("Stack trace should be writable", writableStackTrace);
939                     continue;
940                 }
941                 // You wouldn't believe it, but a call to setStackTrace if non-writable is silently ignored
942                 StackTraceElement[] retrievedStackTrace = e.getStackTrace();
943                 if (retrievedStackTrace.length > 0)
944                 {
945                     assertTrue("stack trace should be writable", writableStackTrace);
946                 }
947             }
948         }
949     }
950 
951     /**
952      * Test the remainder of the EndianUtil class.
953      */
954     @Test
955     public void testEndianUtil()
956     {
957         assertTrue("EndianUtil.BIG_ENDIAN is big endian", EndianUtil.BIG_ENDIAN.isBigEndian());
958         assertFalse("EndianUtil.LITTLE_ENDIAN is not big endian", EndianUtil.LITTLE_ENDIAN.isBigEndian());
959         assertEquals("Platform endianness matches what EndianUtil says", ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN),
960                 EndianUtil.isPlatformBigEndian());
961         assertTrue("EndianUtil.BIG_ENDIAN is big endian", EndianUtil.bigEndian().isBigEndian());
962         assertFalse("EndianUtil.LITTLE_ENDIAN is not big endian", EndianUtil.littleEndian().isBigEndian());
963         assertTrue("EndianUtil has descriptive toString method", EndianUtil.BIG_ENDIAN.toString().startsWith("EndianUtil"));
964     }
965 
966     /**
967      * Test the toString and dataClassName methods of the BasicSerializer.
968      */
969     @Test
970     public void testBasicSerializer()
971     {
972         byte code = 123;
973         String dataClassName = "dataClass";
974         BasicSerializer<Byte> testSerializer = new BasicSerializer<Byte>(code, dataClassName)
975         {
976 
977             @Override
978             public int size(final Byte object) throws SerializationException
979             {
980                 // Auto-generated method stub; never called
981                 return 0;
982             }
983 
984             @Override
985             public int sizeWithPrefix(final Byte object) throws SerializationException
986             {
987                 // Auto-generated method stub; never called
988                 return 0;
989             }
990 
991             @Override
992             public void serialize(final Byte object, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
993                     throws SerializationException
994             {
995                 // Auto-generated method stub; never called
996             }
997 
998             @Override
999             public void serializeWithPrefix(final Byte object, final byte[] buffer, final Pointer pointer,
1000                     final EndianUtil endianUtil) throws SerializationException
1001             {
1002                 // Auto-generated method stub; never called
1003             }
1004 
1005             @Override
1006             public Byte deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1007                     throws SerializationException
1008             {
1009                 // Auto-generated method stub; never called
1010                 return null;
1011             }
1012 
1013             @Override
1014             public int getNumberOfDimensions()
1015             {
1016                 // Auto-generated method stub
1017                 return 0;
1018             }
1019         };
1020         // We only want to test two methods; so we don't have to provide real implementation for other methods
1021         assertEquals("data class name is returned", dataClassName, testSerializer.dataClassName());
1022         assertTrue("toString returns something descriptive", testSerializer.toString().startsWith("BasicSerializer"));
1023     }
1024 
1025 }