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