View Javadoc
1   package org.sim0mq.message;
2   
3   import java.nio.charset.Charset;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import org.djunits.unit.MoneyPerAreaUnit;
8   import org.djunits.unit.MoneyPerDurationUnit;
9   import org.djunits.unit.MoneyPerEnergyUnit;
10  import org.djunits.unit.MoneyPerLengthUnit;
11  import org.djunits.unit.MoneyPerMassUnit;
12  import org.djunits.unit.MoneyPerVolumeUnit;
13  import org.djunits.unit.MoneyUnit;
14  import org.djunits.unit.Unit;
15  import org.djunits.value.StorageType;
16  import org.djunits.value.ValueException;
17  import org.djunits.value.vdouble.matrix.AbstractDoubleMatrix;
18  import org.djunits.value.vdouble.matrix.DoubleMatrixUtil;
19  import org.djunits.value.vdouble.scalar.AbstractDoubleScalar;
20  import org.djunits.value.vdouble.scalar.DoubleScalarUtil;
21  import org.djunits.value.vdouble.scalar.Money;
22  import org.djunits.value.vdouble.scalar.MoneyPerArea;
23  import org.djunits.value.vdouble.scalar.MoneyPerDuration;
24  import org.djunits.value.vdouble.scalar.MoneyPerEnergy;
25  import org.djunits.value.vdouble.scalar.MoneyPerLength;
26  import org.djunits.value.vdouble.scalar.MoneyPerMass;
27  import org.djunits.value.vdouble.scalar.MoneyPerVolume;
28  import org.djunits.value.vdouble.vector.AbstractDoubleVector;
29  import org.djunits.value.vdouble.vector.DoubleVectorUtil;
30  import org.djunits.value.vfloat.matrix.AbstractFloatMatrix;
31  import org.djunits.value.vfloat.matrix.FloatMatrixUtil;
32  import org.djunits.value.vfloat.scalar.AbstractFloatScalar;
33  import org.djunits.value.vfloat.scalar.FloatMoney;
34  import org.djunits.value.vfloat.scalar.FloatMoneyPerArea;
35  import org.djunits.value.vfloat.scalar.FloatMoneyPerDuration;
36  import org.djunits.value.vfloat.scalar.FloatMoneyPerEnergy;
37  import org.djunits.value.vfloat.scalar.FloatMoneyPerLength;
38  import org.djunits.value.vfloat.scalar.FloatMoneyPerMass;
39  import org.djunits.value.vfloat.scalar.FloatMoneyPerVolume;
40  import org.djunits.value.vfloat.scalar.FloatScalarUtil;
41  import org.djunits.value.vfloat.vector.AbstractFloatVector;
42  import org.djunits.value.vfloat.vector.FloatVectorUtil;
43  import org.sim0mq.Sim0MQException;
44  import org.sim0mq.message.types.Sim0MQDisplayType;
45  import org.sim0mq.message.types.Sim0MQTypes;
46  import org.sim0mq.message.types.Sim0MQUnitType;
47  import org.sim0mq.message.util.EndianUtil;
48  
49  import nl.tudelft.simulation.language.Throw;
50  
51  /**
52   * Message conversions. These take into account the endianness for coding the different values. Java is by default big-endian.
53   * <p>
54   * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
55   * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">OpenTrafficSim License</a>.
56   * </p>
57   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
58   * initial version Mar 1, 2017 <br>
59   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
60   */
61  public final class TypedMessage
62  {
63      /** version of the protocol, magic number. */
64      protected static final String VERSION = "SIM01";
65  
66      /** hashcode of Byte class. */
67      protected static final int BYTE_HC = Byte.class.hashCode();
68  
69      /** hashcode of Short class. */
70      protected static final int SHORT_HC = Short.class.hashCode();
71  
72      /** hashcode of Integer class. */
73      protected static final int INTEGER_HC = Integer.class.hashCode();
74  
75      /** hashcode of Long class. */
76      protected static final int LONG_HC = Long.class.hashCode();
77  
78      /** hashcode of Float class. */
79      protected static final int FLOAT_HC = Float.class.hashCode();
80  
81      /** hashcode of Double class. */
82      protected static final int DOUBLE_HC = Double.class.hashCode();
83  
84      /** hashcode of Boolean class. */
85      protected static final int BOOLEAN_HC = Boolean.class.hashCode();
86  
87      /** hashcode of Character class. */
88      protected static final int CHAR_HC = Character.class.hashCode();
89  
90      /** hashcode of String class. */
91      protected static final int STRING_HC = String.class.hashCode();
92  
93      /** hashcode of byte[] class. */
94      protected static final int BYTE_ARRAY_HC = byte[].class.hashCode();
95  
96      /** hashcode of short[] class. */
97      protected static final int SHORT_ARRAY_HC = short[].class.hashCode();
98  
99      /** hashcode of int[] class. */
100     protected static final int INT_ARRAY_HC = int[].class.hashCode();
101 
102     /** hashcode of long[] class. */
103     protected static final int LONG_ARRAY_HC = long[].class.hashCode();
104 
105     /** hashcode of float[] class. */
106     protected static final int FLOAT_ARRAY_HC = float[].class.hashCode();
107 
108     /** hashcode of double[] class. */
109     protected static final int DOUBLE_ARRAY_HC = double[].class.hashCode();
110 
111     /** hashcode of boolean[] class. */
112     protected static final int BOOLEAN_ARRAY_HC = boolean[].class.hashCode();
113 
114     /** hashcode of byte[][] class. */
115     protected static final int BYTE_MATRIX_HC = byte[][].class.hashCode();
116 
117     /** hashcode of short[][] class. */
118     protected static final int SHORT_MATRIX_HC = short[][].class.hashCode();
119 
120     /** hashcode of int[][] class. */
121     protected static final int INT_MATRIX_HC = int[][].class.hashCode();
122 
123     /** hashcode of long[][] class. */
124     protected static final int LONG_MATRIX_HC = long[][].class.hashCode();
125 
126     /** hashcode of float[][] class. */
127     protected static final int FLOAT_MATRIX_HC = float[][].class.hashCode();
128 
129     /** hashcode of double[][] class. */
130     protected static final int DOUBLE_MATRIX_HC = double[][].class.hashCode();
131 
132     /** hashcode of boolean[][] class. */
133     protected static final int BOOLEAN_MATRIX_HC = boolean[][].class.hashCode();
134 
135     /** the UTF-8 charset. */
136     protected static final Charset UTF8 = Charset.forName("UTF-8");
137 
138     /** the UTF-16 charset, big endian variant. */
139     protected static final Charset UTF16 = Charset.forName("UTF-16BE");
140 
141     /**
142      * Do not instantiate this utility class.
143      */
144     private TypedMessage()
145     {
146         // Utility class; do not instantiate.
147     }
148 
149     /**
150      * Encode the object array into a byte[] message. Use UTF8 for the characters and for the String.
151      * @param content the objects to encode
152      * @return the zeroMQ message to send as a byte array
153      * @throws Sim0MQException on unknown data type
154      */
155     public static byte[] encodeUTF8(final Object... content) throws Sim0MQException
156     {
157         return encode(true, false, content);
158     }
159 
160     /**
161      * Encode the object array into a byte[] message. Use UTF16 for the characters and for the String.
162      * @param content the objects to encode
163      * @return the zeroMQ message to send as a byte array
164      * @throws Sim0MQException on unknown data type
165      */
166     public static byte[] encodeUTF16(final Object... content) throws Sim0MQException
167     {
168         return encode(false, false, content);
169     }
170 
171     /**
172      * Encode the object array into a byte[] message. Use UTF8 for the characters and for the String. Make sure the first field
173      * is a String and always encoded as UTF8 (for the "SIM##" magic number at the start).
174      * @param content the objects to encode
175      * @return the zeroMQ message to send as a byte array
176      * @throws Sim0MQException on unknown data type
177      */
178     public static byte[] encode0MQMessageUTF8(final Object... content) throws Sim0MQException
179     {
180         Throw.when(content.length == 0, Sim0MQException.class, "empty array to encode");
181         Throw.when(!(content[1] instanceof String), Sim0MQException.class, "first field in array is not a String");
182         return encode(true, false, content);
183     }
184 
185     /**
186      * Encode the object array into a byte[] message. Use UTF16 for the characters and for the String. Make sure the first field
187      * is a String and always encoded as UTF8 (for the "SIM##" magic number at the start).
188      * @param content the objects to encode
189      * @return the zeroMQ message to send as a byte array
190      * @throws Sim0MQException on unknown data type
191      */
192     public static byte[] encode0MQMessageUTF16(final Object... content) throws Sim0MQException
193     {
194         Throw.when(content.length == 0, Sim0MQException.class, "empty array to encode");
195         Throw.when(!(content[1] instanceof String), Sim0MQException.class, "first field in array is not a String");
196         return encode(false, false, content);
197     }
198 
199     /**
200      * Encode the object array into a Big Endian message.
201      * @param utf8 whether to encode String fields and characters in utf8 or not
202      * @param firstUtf8 whether to encode the first String field in utf8 or not
203      * @param content the objects to encode
204      * @return the zeroMQ message to send as a byte array
205      * @throws Sim0MQException on unknown data type
206      */
207     @SuppressWarnings({ "checkstyle:methodlength", "checkstyle:needbraces" })
208     private static byte[] encode(final boolean utf8, final boolean firstUtf8, final Object... content) throws Sim0MQException
209     {
210         int size = 0;
211         for (int i = 0; i < content.length; i++)
212         {
213             size++; // for the field type
214             int hc = content[i].getClass().hashCode();
215             if (hc == BYTE_HC)
216                 size += 1;
217             else if (hc == SHORT_HC)
218                 size += 2;
219             else if (hc == INTEGER_HC)
220                 size += 4;
221             else if (hc == LONG_HC)
222                 size += 8;
223             else if (hc == FLOAT_HC)
224                 size += 4;
225             else if (hc == DOUBLE_HC)
226                 size += 8;
227             else if (hc == BOOLEAN_HC)
228                 size += 1;
229             else if (hc == CHAR_HC && utf8)
230                 size += 1;
231             else if (hc == CHAR_HC && !utf8)
232                 size += 2;
233             else if (hc == STRING_HC)
234             {
235                 if (utf8 || (i == 0 && firstUtf8))
236                     size += ((String) content[i]).length() + 4;
237                 else
238                     size += 2 * ((String) content[i]).length() + 4;
239             }
240             else if (hc == BYTE_ARRAY_HC)
241                 size += ((byte[]) content[i]).length + 4;
242             else if (hc == SHORT_ARRAY_HC)
243                 size += 2 * ((short[]) content[i]).length + 4;
244             else if (hc == INT_ARRAY_HC)
245                 size += 4 * ((int[]) content[i]).length + 4;
246             else if (hc == LONG_ARRAY_HC)
247                 size += 8 * ((long[]) content[i]).length + 4;
248             else if (hc == FLOAT_ARRAY_HC)
249                 size += 4 * ((float[]) content[i]).length + 4;
250             else if (hc == DOUBLE_ARRAY_HC)
251                 size += 8 * ((double[]) content[i]).length + 4;
252             else if (hc == BOOLEAN_ARRAY_HC)
253                 size += ((boolean[]) content[i]).length + 4;
254             else if (hc == BYTE_MATRIX_HC)
255                 size += ((byte[][]) content[i]).length * ((byte[][]) content[i])[0].length + 8;
256             else if (hc == SHORT_MATRIX_HC)
257                 size += 2 * ((short[][]) content[i]).length * ((short[][]) content[i])[0].length + 8;
258             else if (hc == INT_MATRIX_HC)
259                 size += 4 * ((int[][]) content[i]).length * ((int[][]) content[i])[0].length + 8;
260             else if (hc == LONG_MATRIX_HC)
261                 size += 8 * ((long[][]) content[i]).length * ((long[][]) content[i])[0].length + 8;
262             else if (hc == FLOAT_MATRIX_HC)
263                 size += 4 * ((float[][]) content[i]).length * ((float[][]) content[i])[0].length + 8;
264             else if (hc == DOUBLE_MATRIX_HC)
265                 size += 8 * ((double[][]) content[i]).length * ((double[][]) content[i])[0].length + 8;
266             else if (hc == BOOLEAN_MATRIX_HC)
267                 size += ((boolean[][]) content[i]).length * ((boolean[][]) content[i])[0].length + 8;
268             else if (content[i] instanceof AbstractFloatScalar)
269                 size += 6 + extraBytesMoney(content[i]);
270             else if (content[i] instanceof AbstractDoubleScalar)
271                 size += 10 + extraBytesMoney(content[i]);
272             else if (content[i] instanceof AbstractFloatVector)
273             {
274                 AbstractFloatVector<?, ?> afv = (AbstractFloatVector<?, ?>) content[i];
275                 try
276                 {
277                     size += 4 + 2 + extraBytesMoney(afv.get(0)) + 4 * afv.size();
278                 }
279                 catch (ValueException exception)
280                 {
281                     throw new Sim0MQException(exception);
282                 }
283             }
284             else if (content[i] instanceof AbstractDoubleVector)
285             {
286                 AbstractDoubleVector<?, ?> adv = (AbstractDoubleVector<?, ?>) content[i];
287                 try
288                 {
289                     size += 4 + 2 + extraBytesMoney(adv.get(0)) + 8 * adv.size();
290                 }
291                 catch (ValueException exception)
292                 {
293                     throw new Sim0MQException(exception);
294                 }
295             }
296             else if (content[i] instanceof AbstractFloatMatrix)
297             {
298                 AbstractFloatMatrix<?, ?> afm = (AbstractFloatMatrix<?, ?>) content[i];
299                 try
300                 {
301                     size += 4 + 4 + 2 + extraBytesMoney(afm.get(0, 0)) + 4 * afm.rows() * afm.columns();
302                 }
303                 catch (ValueException exception)
304                 {
305                     throw new Sim0MQException(exception);
306                 }
307             }
308             else if (content[i] instanceof AbstractDoubleMatrix)
309             {
310                 AbstractDoubleMatrix<?, ?> adm = (AbstractDoubleMatrix<?, ?>) content[i];
311                 try
312                 {
313                     size += 4 + 4 + 2 + extraBytesMoney(adm.get(0, 0)) + 8 * adm.rows() * adm.columns();
314                 }
315                 catch (ValueException exception)
316                 {
317                     throw new Sim0MQException(exception);
318                 }
319             }
320             else if (content[i] instanceof AbstractFloatVector[])
321             {
322                 AbstractFloatVector<?, ?>[] afvArray = (AbstractFloatVector<?, ?>[]) content[i];
323                 try
324                 {
325                     size += 4 + 4; // rows, cols
326                     for (int j = 0; j < afvArray.length; j++)
327                     {
328                         size += 2 + extraBytesMoney(afvArray[j].get(0)) + 4 * afvArray[j].size();
329                     }
330                 }
331                 catch (ValueException exception)
332                 {
333                     throw new Sim0MQException(exception);
334                 }
335             }
336             else if (content[i] instanceof AbstractDoubleVector[])
337             {
338                 AbstractDoubleVector<?, ?>[] afvArray = (AbstractDoubleVector<?, ?>[]) content[i];
339                 try
340                 {
341                     size += 4 + 4; // rows, cols
342                     for (int j = 0; j < afvArray.length; j++)
343                     {
344                         size += 2 + extraBytesMoney(afvArray[j].get(0)) + 8 * afvArray[j].size();
345                     }
346                 }
347                 catch (ValueException exception)
348                 {
349                     throw new Sim0MQException(exception);
350                 }
351             }
352             else
353                 throw new Sim0MQException("Unknown data type " + content[i].getClass() + " for encoding the ZeroMQ message");
354         }
355 
356         byte[] message = new byte[size];
357         int pointer = 0;
358 
359         for (int i = 0; i < content.length; i++)
360         {
361             Object field = content[i];
362             int hc = field.getClass().hashCode();
363 
364             if (hc == BYTE_HC)
365             {
366                 message[pointer++] = Sim0MQTypes.BYTE_8;
367                 message[pointer++] = (byte) field;
368             }
369 
370             else if (hc == SHORT_HC)
371             {
372                 message[pointer++] = Sim0MQTypes.SHORT_16;
373                 short v = (short) field;
374                 pointer = EndianUtil.encodeShort(v, message, pointer);
375             }
376 
377             else if (hc == INTEGER_HC)
378             {
379                 message[pointer++] = Sim0MQTypes.INT_32;
380                 int v = (int) field;
381                 pointer = EndianUtil.encodeInt(v, message, pointer);
382             }
383 
384             else if (hc == LONG_HC)
385             {
386                 message[pointer++] = Sim0MQTypes.LONG_64;
387                 long v = (long) field;
388                 pointer = EndianUtil.encodeLong(v, message, pointer);
389             }
390 
391             else if (hc == FLOAT_HC)
392             {
393                 message[pointer++] = Sim0MQTypes.FLOAT_32;
394                 float v = (float) field;
395                 pointer = EndianUtil.encodeFloat(v, message, pointer);
396             }
397 
398             else if (hc == DOUBLE_HC)
399             {
400                 message[pointer++] = Sim0MQTypes.DOUBLE_64;
401                 double v = (double) field;
402                 pointer = EndianUtil.encodeDouble(v, message, pointer);
403             }
404 
405             else if (hc == BOOLEAN_HC)
406             {
407                 message[pointer++] = Sim0MQTypes.BOOLEAN_8;
408                 message[pointer++] = (byte) ((boolean) field ? 1 : 0);
409             }
410 
411             else if (hc == CHAR_HC && utf8)
412             {
413                 message[pointer++] = Sim0MQTypes.CHAR_8;
414                 char v = (char) field;
415                 message[pointer++] = (byte) (v & 0xFF);
416             }
417 
418             else if (hc == CHAR_HC && !utf8)
419             {
420                 message[pointer++] = Sim0MQTypes.CHAR_16;
421                 char v = (char) field;
422                 pointer = EndianUtil.encodeChar(v, message, pointer);
423             }
424 
425             else if (hc == STRING_HC && utf8)
426             {
427                 message[pointer++] = Sim0MQTypes.STRING_8;
428                 int len = ((String) field).length();
429                 pointer = EndianUtil.encodeInt(len, message, pointer);
430                 byte[] s = ((String) field).getBytes(UTF8);
431                 for (byte b : s)
432                 {
433                     message[pointer++] = b;
434                 }
435             }
436 
437             else if (hc == STRING_HC && !utf8)
438             {
439                 message[pointer++] = Sim0MQTypes.STRING_16;
440                 int len = ((String) field).length();
441                 pointer = EndianUtil.encodeInt(len, message, pointer);
442                 byte[] s = ((String) field).getBytes(UTF16);
443                 for (byte b : s)
444                 {
445                     message[pointer++] = b;
446                 }
447             }
448 
449             else if (hc == BYTE_ARRAY_HC)
450             {
451                 message[pointer++] = Sim0MQTypes.BYTE_8_ARRAY;
452                 int len = ((byte[]) field).length;
453                 pointer = EndianUtil.encodeInt(len, message, pointer);
454                 for (byte v : (byte[]) field)
455                 {
456                     message[pointer++] = v;
457                 }
458             }
459 
460             else if (hc == SHORT_ARRAY_HC)
461             {
462                 message[pointer++] = Sim0MQTypes.SHORT_16_ARRAY;
463                 int len = ((short[]) field).length;
464                 pointer = EndianUtil.encodeInt(len, message, pointer);
465                 for (short v : (short[]) field)
466                 {
467                     pointer = EndianUtil.encodeShort(v, message, pointer);
468                 }
469             }
470 
471             else if (hc == INT_ARRAY_HC)
472             {
473                 message[pointer++] = Sim0MQTypes.INT_32_ARRAY;
474                 int len = ((int[]) field).length;
475                 pointer = EndianUtil.encodeInt(len, message, pointer);
476                 for (int v : (int[]) field)
477                 {
478                     pointer = EndianUtil.encodeInt(v, message, pointer);
479                 }
480             }
481 
482             else if (hc == LONG_ARRAY_HC)
483             {
484                 message[pointer++] = Sim0MQTypes.LONG_64_ARRAY;
485                 int len = ((long[]) field).length;
486                 pointer = EndianUtil.encodeInt(len, message, pointer);
487                 for (long v : (long[]) field)
488                 {
489                     pointer = EndianUtil.encodeLong(v, message, pointer);
490                 }
491             }
492 
493             else if (hc == FLOAT_ARRAY_HC)
494             {
495                 message[pointer++] = Sim0MQTypes.FLOAT_32_ARRAY;
496                 int len = ((float[]) field).length;
497                 pointer = EndianUtil.encodeInt(len, message, pointer);
498                 for (float v : (float[]) field)
499                 {
500                     pointer = EndianUtil.encodeFloat(v, message, pointer);
501                 }
502             }
503 
504             else if (hc == DOUBLE_ARRAY_HC)
505             {
506                 message[pointer++] = Sim0MQTypes.DOUBLE_64_ARRAY;
507                 int len = ((double[]) field).length;
508                 pointer = EndianUtil.encodeInt(len, message, pointer);
509                 for (double v : (double[]) field)
510                 {
511                     pointer = EndianUtil.encodeDouble(v, message, pointer);
512                 }
513             }
514 
515             else if (hc == BOOLEAN_ARRAY_HC)
516             {
517                 message[pointer++] = Sim0MQTypes.BOOLEAN_8_ARRAY;
518                 int len = ((boolean[]) field).length;
519                 pointer = EndianUtil.encodeInt(len, message, pointer);
520                 for (boolean v : (boolean[]) field)
521                 {
522                     message[pointer++] = (byte) (v ? 1 : 0);
523                 }
524             }
525 
526             else if (hc == BYTE_MATRIX_HC)
527             {
528                 message[pointer++] = Sim0MQTypes.BYTE_8_MATRIX;
529                 byte[][] matrix = (byte[][]) field;
530                 int rows = matrix.length;
531                 pointer = EndianUtil.encodeInt(rows, message, pointer);
532                 int cols = matrix[0].length;
533                 pointer = EndianUtil.encodeInt(cols, message, pointer);
534                 for (int row = 0; row < rows; row++)
535                 {
536                     byte[] vRow = matrix[row];
537                     for (byte v : vRow)
538                     {
539                         message[pointer++] = v;
540                     }
541                 }
542             }
543 
544             else if (hc == SHORT_MATRIX_HC)
545             {
546                 message[pointer++] = Sim0MQTypes.SHORT_16_MATRIX;
547                 short[][] matrix = (short[][]) field;
548                 int rows = matrix.length;
549                 pointer = EndianUtil.encodeInt(rows, message, pointer);
550                 int cols = matrix[0].length;
551                 pointer = EndianUtil.encodeInt(cols, message, pointer);
552                 for (int row = 0; row < rows; row++)
553                 {
554                     short[] vRow = matrix[row];
555                     for (short v : vRow)
556                     {
557                         pointer = EndianUtil.encodeShort(v, message, pointer);
558                     }
559                 }
560             }
561 
562             else if (hc == INT_MATRIX_HC)
563             {
564                 message[pointer++] = Sim0MQTypes.INT_32_MATRIX;
565                 int[][] matrix = (int[][]) field;
566                 int rows = matrix.length;
567                 pointer = EndianUtil.encodeInt(rows, message, pointer);
568                 int cols = matrix[0].length;
569                 pointer = EndianUtil.encodeInt(cols, message, pointer);
570                 for (int row = 0; row < rows; row++)
571                 {
572                     int[] vRow = matrix[row];
573                     for (int v : vRow)
574                     {
575                         pointer = EndianUtil.encodeInt(v, message, pointer);
576                     }
577                 }
578             }
579 
580             else if (hc == LONG_MATRIX_HC)
581             {
582                 message[pointer++] = Sim0MQTypes.LONG_64_MATRIX;
583                 long[][] matrix = (long[][]) field;
584                 int rows = matrix.length;
585                 pointer = EndianUtil.encodeInt(rows, message, pointer);
586                 int cols = matrix[0].length;
587                 pointer = EndianUtil.encodeInt(cols, message, pointer);
588                 for (int row = 0; row < rows; row++)
589                 {
590                     long[] vRow = matrix[row];
591                     for (long v : vRow)
592                     {
593                         pointer = EndianUtil.encodeLong(v, message, pointer);
594                     }
595                 }
596             }
597 
598             else if (hc == FLOAT_MATRIX_HC)
599             {
600                 message[pointer++] = Sim0MQTypes.FLOAT_32_MATRIX;
601                 float[][] matrix = (float[][]) field;
602                 int rows = matrix.length;
603                 pointer = EndianUtil.encodeInt(rows, message, pointer);
604                 int cols = matrix[0].length;
605                 pointer = EndianUtil.encodeInt(cols, message, pointer);
606                 for (int row = 0; row < rows; row++)
607                 {
608                     float[] vRow = matrix[row];
609                     for (float v : vRow)
610                     {
611                         pointer = EndianUtil.encodeFloat(v, message, pointer);
612                     }
613                 }
614             }
615 
616             else if (hc == DOUBLE_MATRIX_HC)
617             {
618                 message[pointer++] = Sim0MQTypes.DOUBLE_64_MATRIX;
619                 double[][] matrix = (double[][]) field;
620                 int rows = matrix.length;
621                 pointer = EndianUtil.encodeInt(rows, message, pointer);
622                 int cols = matrix[0].length;
623                 pointer = EndianUtil.encodeInt(cols, message, pointer);
624                 for (int row = 0; row < rows; row++)
625                 {
626                     double[] vRow = matrix[row];
627                     for (double v : vRow)
628                     {
629                         pointer = EndianUtil.encodeDouble(v, message, pointer);
630                     }
631                 }
632             }
633 
634             else if (hc == BOOLEAN_MATRIX_HC)
635             {
636                 message[pointer++] = Sim0MQTypes.BOOLEAN_8_MATRIX;
637                 boolean[][] matrix = (boolean[][]) field;
638                 int rows = matrix.length;
639                 pointer = EndianUtil.encodeInt(rows, message, pointer);
640                 int cols = matrix[0].length;
641                 pointer = EndianUtil.encodeInt(cols, message, pointer);
642                 for (int row = 0; row < rows; row++)
643                 {
644                     boolean[] vRow = matrix[row];
645                     for (boolean v : vRow)
646                     {
647                         message[pointer++] = (byte) (v ? 1 : 0);
648                     }
649                 }
650             }
651 
652             else if (field instanceof AbstractFloatScalar)
653             {
654                 message[pointer++] = Sim0MQTypes.FLOAT_32_UNIT;
655                 pointer = encodeUnit(((AbstractFloatScalar<?, ?>) field).getUnit(), message, pointer);
656                 float v = ((AbstractFloatScalar<?, ?>) field).si;
657                 pointer = EndianUtil.encodeFloat(v, message, pointer);
658             }
659 
660             else if (content[i] instanceof AbstractDoubleScalar)
661             {
662                 message[pointer++] = Sim0MQTypes.DOUBLE_64_UNIT;
663                 pointer = encodeUnit(((AbstractDoubleScalar<?, ?>) field).getUnit(), message, pointer);
664                 double v = ((AbstractDoubleScalar<?, ?>) field).si;
665                 pointer = EndianUtil.encodeDouble(v, message, pointer);
666             }
667 
668             else if (content[i] instanceof AbstractFloatVector)
669             {
670                 message[pointer++] = Sim0MQTypes.FLOAT_32_UNIT_ARRAY;
671                 AbstractFloatVector<?, ?> afv = (AbstractFloatVector<?, ?>) content[i];
672                 pointer = EndianUtil.encodeInt(afv.size(), message, pointer);
673                 pointer = encodeUnit(afv.getUnit(), message, pointer);
674                 try
675                 {
676                     for (int j = 0; j < afv.size(); j++)
677                     {
678                         pointer = EndianUtil.encodeFloat(afv.getSI(j), message, pointer);
679                     }
680                 }
681                 catch (ValueException exception)
682                 {
683                     throw new Sim0MQException(exception);
684                 }
685             }
686 
687             else if (content[i] instanceof AbstractDoubleVector)
688             {
689                 message[pointer++] = Sim0MQTypes.DOUBLE_64_UNIT_ARRAY;
690                 AbstractDoubleVector<?, ?> adv = (AbstractDoubleVector<?, ?>) content[i];
691                 pointer = EndianUtil.encodeInt(adv.size(), message, pointer);
692                 pointer = encodeUnit(adv.getUnit(), message, pointer);
693                 try
694                 {
695                     for (int j = 0; j < adv.size(); j++)
696                     {
697                         pointer = EndianUtil.encodeDouble(adv.getSI(j), message, pointer);
698                     }
699                 }
700                 catch (ValueException exception)
701                 {
702                     throw new Sim0MQException(exception);
703                 }
704             }
705 
706             else if (content[i] instanceof AbstractFloatMatrix)
707             {
708                 message[pointer++] = Sim0MQTypes.FLOAT_32_UNIT_MATRIX;
709                 AbstractFloatMatrix<?, ?> afm = (AbstractFloatMatrix<?, ?>) content[i];
710                 pointer = EndianUtil.encodeInt(afm.rows(), message, pointer);
711                 pointer = EndianUtil.encodeInt(afm.columns(), message, pointer);
712                 pointer = encodeUnit(afm.getUnit(), message, pointer);
713                 try
714                 {
715                     for (int row = 0; row < afm.rows(); row++)
716                     {
717                         for (int col = 0; col < afm.columns(); col++)
718                         {
719                             pointer = EndianUtil.encodeFloat(afm.getSI(row, col), message, pointer);
720                         }
721                     }
722                 }
723                 catch (ValueException exception)
724                 {
725                     throw new Sim0MQException(exception);
726                 }
727             }
728 
729             else if (content[i] instanceof AbstractDoubleMatrix)
730             {
731                 message[pointer++] = Sim0MQTypes.DOUBLE_64_UNIT_MATRIX;
732                 AbstractDoubleMatrix<?, ?> adm = (AbstractDoubleMatrix<?, ?>) content[i];
733                 pointer = EndianUtil.encodeInt(adm.rows(), message, pointer);
734                 pointer = EndianUtil.encodeInt(adm.columns(), message, pointer);
735                 pointer = encodeUnit(adm.getUnit(), message, pointer);
736                 try
737                 {
738                     for (int row = 0; row < adm.rows(); row++)
739                     {
740                         for (int col = 0; col < adm.columns(); col++)
741                         {
742                             pointer = EndianUtil.encodeDouble(adm.getSI(row, col), message, pointer);
743                         }
744                     }
745                 }
746                 catch (ValueException exception)
747                 {
748                     throw new Sim0MQException(exception);
749                 }
750             }
751 
752             else if (content[i] instanceof AbstractFloatVector[])
753             {
754                 message[pointer++] = Sim0MQTypes.FLOAT_32_UNIT_COLUMN_ARRAY;
755                 AbstractFloatVector<?, ?>[] afvArray = (AbstractFloatVector<?, ?>[]) content[i];
756                 pointer = EndianUtil.encodeInt(afvArray[0].size(), message, pointer); // rows
757                 pointer = EndianUtil.encodeInt(afvArray.length, message, pointer); // cols
758                 for (int col = 0; col < afvArray.length; col++)
759                 {
760                     pointer = encodeUnit(afvArray[col].getUnit(), message, pointer);
761                 }
762                 try
763                 {
764                     for (int row = 0; row < afvArray[0].size(); row++)
765                     {
766                         for (int col = 0; col < afvArray.length; col++)
767                         {
768                             pointer = EndianUtil.encodeFloat(afvArray[col].getSI(row), message, pointer);
769                         }
770                     }
771                 }
772                 catch (ValueException exception)
773                 {
774                     throw new Sim0MQException(exception);
775                 }
776             }
777 
778             else if (content[i] instanceof AbstractDoubleVector[])
779             {
780                 message[pointer++] = Sim0MQTypes.DOUBLE_64_UNIT_COLUMN_ARRAY;
781                 AbstractDoubleVector<?, ?>[] advArray = (AbstractDoubleVector<?, ?>[]) content[i];
782                 pointer = EndianUtil.encodeInt(advArray[0].size(), message, pointer); // rows
783                 pointer = EndianUtil.encodeInt(advArray.length, message, pointer); // cols
784                 for (int col = 0; col < advArray.length; col++)
785                 {
786                     pointer = encodeUnit(advArray[col].getUnit(), message, pointer);
787                 }
788                 try
789                 {
790                     for (int row = 0; row < advArray[0].size(); row++)
791                     {
792                         for (int col = 0; col < advArray.length; col++)
793                         {
794                             pointer = EndianUtil.encodeDouble(advArray[col].getSI(row), message, pointer);
795                         }
796                     }
797                 }
798                 catch (ValueException exception)
799                 {
800                     throw new Sim0MQException(exception);
801                 }
802             }
803 
804             else
805                 throw new Sim0MQException("Unknown data type " + content[i].getClass() + " for encoding the ZeroMQ message");
806 
807         }
808 
809         return message;
810     }
811 
812     /**
813      * Code a unit, including MoneyUnits.
814      * @param unit the unit to code in the byte array
815      * @param message the byte array
816      * @param pointer the start pointer in the byte array
817      * @return the new pointer in the byte array
818      */
819     @SuppressWarnings("rawtypes")
820     private static int encodeUnit(final Unit unit, final byte[] message, final int pointer)
821     {
822         int p = pointer;
823         @SuppressWarnings("unchecked") // TODO see how this can be solved with type <U extends Unit<U>>
824         Sim0MQUnitType unitType = Sim0MQUnitType.getUnitType(unit);
825         message[p++] = unitType.getCode();
826         if (unit instanceof MoneyUnit)
827         {
828             @SuppressWarnings("unchecked")
829             Sim0MQDisplayType displayType = Sim0MQDisplayType.getDisplayType(unit);
830             p = EndianUtil.encodeShort((short) displayType.getIntCode(), message, p);
831         }
832         else if (unit instanceof MoneyPerAreaUnit)
833         {
834             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerAreaUnit) unit).getMoneyUnit());
835             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
836             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerAreaUnit) unit).getAreaUnit());
837             message[p++] = perType.getByteCode();
838         }
839         else if (unit instanceof MoneyPerEnergyUnit)
840         {
841             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerEnergyUnit) unit).getMoneyUnit());
842             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
843             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerEnergyUnit) unit).getEnergyUnit());
844             message[p++] = perType.getByteCode();
845         }
846         else if (unit instanceof MoneyPerLengthUnit)
847         {
848             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerLengthUnit) unit).getMoneyUnit());
849             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
850             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerLengthUnit) unit).getLengthUnit());
851             message[p++] = perType.getByteCode();
852         }
853         else if (unit instanceof MoneyPerMassUnit)
854         {
855             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerMassUnit) unit).getMoneyUnit());
856             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
857             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerMassUnit) unit).getMassUnit());
858             message[p++] = perType.getByteCode();
859         }
860         else if (unit instanceof MoneyPerDurationUnit)
861         {
862             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerDurationUnit) unit).getMoneyUnit());
863             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
864             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerDurationUnit) unit).getDurationUnit());
865             message[p++] = perType.getByteCode();
866         }
867         else if (unit instanceof MoneyPerVolumeUnit)
868         {
869             Sim0MQDisplayType moneyType = Sim0MQDisplayType.getDisplayType(((MoneyPerVolumeUnit) unit).getMoneyUnit());
870             p = EndianUtil.encodeShort((short) moneyType.getIntCode(), message, p);
871             Sim0MQDisplayType perType = Sim0MQDisplayType.getDisplayType(((MoneyPerVolumeUnit) unit).getVolumeUnit());
872             message[p++] = perType.getByteCode();
873         }
874         else
875         {
876             @SuppressWarnings("unchecked")
877             Sim0MQDisplayType displayType = Sim0MQDisplayType.getDisplayType(unit);
878             message[p++] = displayType.getByteCode();
879         }
880         return p;
881     }
882 
883     /**
884      * Decode the message into an object array.
885      * @param message the ZeroMQ byte array to decode
886      * @return an array of objects of the right type
887      * @throws Sim0MQException on unknown data type
888      */
889     @SuppressWarnings({ "checkstyle:methodlength", "checkstyle:needbraces" })
890     public static Object[] decodeSim0MQMessage(final byte[] message) throws Sim0MQException
891     {
892         Throw.when(message.length < 10, Sim0MQException.class, "Message length < 10");
893         Object[] array = decode(message);
894 
895         // check the magic number
896         byte char0 = message[0];
897         Throw.when(char0 != 9, Sim0MQException.class, "Message does not start with an UTF8 string");
898         int magicLen = EndianUtil.decodeInt(message, 1);
899         Throw.when(magicLen < 0, Sim0MQException.class, "Length of magic number < 0");
900         Throw.when(Double.isNaN(magicLen), Sim0MQException.class, "Length of magic number = NaN");
901         Throw.when(Double.isInfinite(magicLen), Sim0MQException.class, "Length of magic number = Infinite");
902         Throw.when(magicLen > 10, Sim0MQException.class, "Length of magic number > 10");
903         String magicNumber = EndianUtil.decodeUTF8String(message, 1);
904         Throw.when(!magicNumber.startsWith("SIM"), Sim0MQException.class,
905                 "Magic number does not start with SIM but with " + magicNumber);
906         Throw.when(!magicNumber.equals(VERSION), Sim0MQException.class,
907                 "Message version " + magicNumber + " not compatible with this software version " + VERSION);
908 
909         return array;
910     }
911 
912     /**
913      * Decode the message into an object array.
914      * @param message the ZeroMQ byte array to decode
915      * @return an array of objects of the right type
916      * @throws Sim0MQException on unknown data type
917      */
918     @SuppressWarnings({ "checkstyle:methodlength", "checkstyle:needbraces" })
919     public static Object[] decode(final byte[] message) throws Sim0MQException
920     {
921         List<Object> list = new ArrayList<>();
922         MessageBuffer mb = new MessageBuffer(message);
923         while (mb.hasMore())
924         {
925             byte type = mb.getByte();
926 
927             if (type == Sim0MQTypes.BYTE_8)
928             {
929                 list.add(mb.getByte());
930             }
931 
932             else if (type == Sim0MQTypes.SHORT_16)
933             {
934                 list.add(mb.getShort());
935             }
936 
937             else if (type == Sim0MQTypes.INT_32)
938             {
939                 list.add(mb.getInt());
940             }
941 
942             else if (type == Sim0MQTypes.LONG_64)
943             {
944                 list.add(mb.getLong());
945             }
946 
947             else if (type == Sim0MQTypes.FLOAT_32)
948             {
949                 list.add(mb.getFloat());
950             }
951 
952             else if (type == Sim0MQTypes.DOUBLE_64)
953             {
954                 list.add(mb.getDouble());
955             }
956 
957             else if (type == Sim0MQTypes.BOOLEAN_8)
958             {
959                 list.add(mb.getBoolean());
960             }
961 
962             else if (type == Sim0MQTypes.CHAR_8)
963             {
964                 list.add(mb.getCharUTF8());
965             }
966 
967             else if (type == Sim0MQTypes.CHAR_16)
968             {
969                 list.add(mb.getCharUTF16());
970             }
971 
972             else if (type == Sim0MQTypes.STRING_8)
973             {
974                 list.add(mb.getStringUTF8());
975             }
976 
977             else if (type == Sim0MQTypes.STRING_16)
978             {
979                 list.add(mb.getStringUTF16());
980             }
981 
982             else if (type == Sim0MQTypes.BYTE_8_ARRAY)
983             {
984                 int size = mb.getInt();
985                 byte[] value = new byte[size];
986                 for (int i = 0; i < size; i++)
987                     value[i] = mb.getByte();
988                 list.add(value);
989             }
990 
991             else if (type == Sim0MQTypes.SHORT_16_ARRAY)
992             {
993                 int size = mb.getInt();
994                 short[] value = new short[size];
995                 for (int i = 0; i < size; i++)
996                     value[i] = mb.getShort();
997                 list.add(value);
998             }
999 
1000             else if (type == Sim0MQTypes.INT_32_ARRAY)
1001             {
1002                 int size = mb.getInt();
1003                 int[] value = new int[size];
1004                 for (int i = 0; i < size; i++)
1005                     value[i] = mb.getInt();
1006                 list.add(value);
1007             }
1008 
1009             else if (type == Sim0MQTypes.LONG_64_ARRAY)
1010             {
1011                 int size = mb.getInt();
1012                 long[] value = new long[size];
1013                 for (int i = 0; i < size; i++)
1014                     value[i] = mb.getLong();
1015                 list.add(value);
1016             }
1017 
1018             else if (type == Sim0MQTypes.FLOAT_32_ARRAY)
1019             {
1020                 int size = mb.getInt();
1021                 float[] value = new float[size];
1022                 for (int i = 0; i < size; i++)
1023                     value[i] = mb.getFloat();
1024                 list.add(value);
1025             }
1026 
1027             else if (type == Sim0MQTypes.DOUBLE_64_ARRAY)
1028             {
1029                 int size = mb.getInt();
1030                 double[] value = new double[size];
1031                 for (int i = 0; i < size; i++)
1032                     value[i] = mb.getDouble();
1033                 list.add(value);
1034             }
1035 
1036             else if (type == Sim0MQTypes.BOOLEAN_8_ARRAY)
1037             {
1038                 int size = mb.getInt();
1039                 boolean[] value = new boolean[size];
1040                 for (int i = 0; i < size; i++)
1041                     value[i] = mb.getBoolean();
1042                 list.add(value);
1043             }
1044 
1045             else if (type == Sim0MQTypes.BYTE_8_MATRIX)
1046             {
1047                 int rows = mb.getInt();
1048                 int cols = mb.getInt();
1049                 byte[][] value = new byte[rows][];
1050                 for (int row = 0; row < rows; row++)
1051                 {
1052                     for (int col = 0; col < cols; col++)
1053                     {
1054                         if (col == 0)
1055                             value[row] = new byte[cols];
1056                         value[row][col] = mb.getByte();
1057                     }
1058                 }
1059                 list.add(value);
1060             }
1061 
1062             else if (type == Sim0MQTypes.SHORT_16_MATRIX)
1063             {
1064                 int rows = mb.getInt();
1065                 int cols = mb.getInt();
1066                 short[][] value = new short[rows][];
1067                 for (int row = 0; row < rows; row++)
1068                 {
1069                     for (int col = 0; col < cols; col++)
1070                     {
1071                         if (col == 0)
1072                             value[row] = new short[cols];
1073                         value[row][col] = mb.getShort();
1074                     }
1075                 }
1076                 list.add(value);
1077             }
1078 
1079             else if (type == Sim0MQTypes.INT_32_MATRIX)
1080             {
1081                 int rows = mb.getInt();
1082                 int cols = mb.getInt();
1083                 int[][] value = new int[rows][];
1084                 for (int row = 0; row < rows; row++)
1085                 {
1086                     for (int col = 0; col < cols; col++)
1087                     {
1088                         if (col == 0)
1089                             value[row] = new int[cols];
1090                         value[row][col] = mb.getInt();
1091                     }
1092                 }
1093                 list.add(value);
1094             }
1095 
1096             else if (type == Sim0MQTypes.LONG_64_MATRIX)
1097             {
1098                 int rows = mb.getInt();
1099                 int cols = mb.getInt();
1100                 long[][] value = new long[rows][];
1101                 for (int row = 0; row < rows; row++)
1102                 {
1103                     for (int col = 0; col < cols; col++)
1104                     {
1105                         if (col == 0)
1106                             value[row] = new long[cols];
1107                         value[row][col] = mb.getLong();
1108                     }
1109                 }
1110                 list.add(value);
1111             }
1112 
1113             else if (type == Sim0MQTypes.FLOAT_32_MATRIX)
1114             {
1115                 int rows = mb.getInt();
1116                 int cols = mb.getInt();
1117                 float[][] value = new float[rows][];
1118                 for (int row = 0; row < rows; row++)
1119                 {
1120                     for (int col = 0; col < cols; col++)
1121                     {
1122                         if (col == 0)
1123                             value[row] = new float[cols];
1124                         value[row][col] = mb.getFloat();
1125                     }
1126                 }
1127                 list.add(value);
1128             }
1129 
1130             else if (type == Sim0MQTypes.DOUBLE_64_MATRIX)
1131             {
1132                 int rows = mb.getInt();
1133                 int cols = mb.getInt();
1134                 double[][] value = new double[rows][];
1135                 for (int row = 0; row < rows; row++)
1136                 {
1137                     for (int col = 0; col < cols; col++)
1138                     {
1139                         if (col == 0)
1140                             value[row] = new double[cols];
1141                         value[row][col] = mb.getDouble();
1142                     }
1143                 }
1144                 list.add(value);
1145             }
1146 
1147             else if (type == Sim0MQTypes.BOOLEAN_8_MATRIX)
1148             {
1149                 int rows = mb.getInt();
1150                 int cols = mb.getInt();
1151                 boolean[][] value = new boolean[rows][];
1152                 for (int row = 0; row < rows; row++)
1153                 {
1154                     for (int col = 0; col < cols; col++)
1155                     {
1156                         if (col == 0)
1157                             value[row] = new boolean[cols];
1158                         value[row][col] = mb.getBoolean();
1159                     }
1160                 }
1161                 list.add(value);
1162             }
1163 
1164             else if (type == Sim0MQTypes.FLOAT_32_UNIT)
1165             {
1166                 Unit<? extends Unit<?>> unit = mb.getUnit();
1167                 float si = mb.getFloat();
1168                 list.add(FloatScalarUtil.instantiateAnonymousSI(si, unit));
1169             }
1170 
1171             else if (type == Sim0MQTypes.DOUBLE_64_UNIT)
1172             {
1173                 Unit<? extends Unit<?>> unit = mb.getUnit();
1174                 double si = mb.getDouble();
1175                 list.add(DoubleScalarUtil.instantiateAnonymousSI(si, unit));
1176             }
1177 
1178             else if (type == Sim0MQTypes.FLOAT_32_UNIT_ARRAY)
1179             {
1180                 int size = mb.getInt();
1181                 Unit<? extends Unit<?>> unit = mb.getUnit();
1182                 float[] array = new float[size];
1183                 for (int i = 0; i < size; i++)
1184                     array[i] = mb.getFloat();
1185                 try
1186                 {
1187                     list.add(FloatVectorUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE));
1188                 }
1189                 catch (ValueException exception)
1190                 {
1191                     throw new Sim0MQException(exception);
1192                 }
1193             }
1194 
1195             else if (type == Sim0MQTypes.DOUBLE_64_UNIT_ARRAY)
1196             {
1197                 int size = mb.getInt();
1198                 Unit<? extends Unit<?>> unit = mb.getUnit();
1199                 double[] array = new double[size];
1200                 for (int i = 0; i < size; i++)
1201                     array[i] = mb.getDouble();
1202                 try
1203                 {
1204                     list.add(DoubleVectorUtil.instantiateAnonymousSI(array, unit, StorageType.DENSE));
1205                 }
1206                 catch (ValueException exception)
1207                 {
1208                     throw new Sim0MQException(exception);
1209                 }
1210             }
1211 
1212             else if (type == Sim0MQTypes.FLOAT_32_UNIT_MATRIX)
1213             {
1214                 int rows = mb.getInt();
1215                 int cols = mb.getInt();
1216                 Unit<? extends Unit<?>> unit = mb.getUnit();
1217                 float[][] matrix = new float[rows][cols];
1218                 for (int row = 0; row < rows; row++)
1219                 {
1220                     for (int col = 0; col < cols; col++)
1221                     {
1222                         if (col == 0)
1223                             matrix[row] = new float[cols];
1224                         matrix[row][col] = mb.getFloat();
1225                     }
1226                 }
1227                 try
1228                 {
1229                     list.add(FloatMatrixUtil.instantiateAnonymousSI(matrix, unit, StorageType.DENSE));
1230                 }
1231                 catch (ValueException exception)
1232                 {
1233                     throw new Sim0MQException(exception);
1234                 }
1235             }
1236 
1237             else if (type == Sim0MQTypes.DOUBLE_64_UNIT_MATRIX)
1238             {
1239                 int rows = mb.getInt();
1240                 int cols = mb.getInt();
1241                 Unit<? extends Unit<?>> unit = mb.getUnit();
1242                 double[][] matrix = new double[rows][cols];
1243                 for (int row = 0; row < rows; row++)
1244                 {
1245                     for (int col = 0; col < cols; col++)
1246                     {
1247                         if (col == 0)
1248                             matrix[row] = new double[cols];
1249                         matrix[row][col] = mb.getDouble();
1250                     }
1251                 }
1252                 try
1253                 {
1254                     list.add(DoubleMatrixUtil.instantiateAnonymousSI(matrix, unit, StorageType.DENSE));
1255                 }
1256                 catch (ValueException exception)
1257                 {
1258                     throw new Sim0MQException(exception);
1259                 }
1260             }
1261 
1262             else if (type == Sim0MQTypes.FLOAT_32_UNIT_COLUMN_ARRAY)
1263             {
1264                 int rows = mb.getInt();
1265                 int cols = mb.getInt();
1266                 Unit<? extends Unit<?>>[] units = new Unit<?>[cols];
1267                 AbstractFloatVector<?, ?>[] vArray = new AbstractFloatVector[cols];
1268                 for (int col = 0; col < cols; col++)
1269                 {
1270                     units[col] = mb.getUnit();
1271                 }
1272                 // here we use a column-first matrix (!) for storage
1273                 float[][] matrix = new float[cols][rows];
1274                 for (int row = 0; row < rows; row++)
1275                 {
1276                     for (int col = 0; col < cols; col++)
1277                     {
1278                         if (col == 0)
1279                             matrix[row] = new float[cols];
1280                         matrix[col][row] = mb.getFloat();
1281                     }
1282                 }
1283                 try
1284                 {
1285                     for (int col = 0; col < cols; col++)
1286                     {
1287                         vArray[col] = FloatVectorUtil.instantiateAnonymousSI(matrix[col], units[col], StorageType.DENSE);
1288                     }
1289                     list.add(vArray);
1290                 }
1291                 catch (ValueException exception)
1292                 {
1293                     throw new Sim0MQException(exception);
1294                 }
1295             }
1296 
1297             else if (type == Sim0MQTypes.DOUBLE_64_UNIT_COLUMN_ARRAY)
1298             {
1299                 int rows = mb.getInt();
1300                 int cols = mb.getInt();
1301                 Unit<? extends Unit<?>>[] units = new Unit<?>[cols];
1302                 AbstractDoubleVector<?, ?>[] vArray = new AbstractDoubleVector[cols];
1303                 for (int col = 0; col < cols; col++)
1304                 {
1305                     units[col] = mb.getUnit();
1306                 }
1307                 // here we use a column-first matrix (!) for storage
1308                 double[][] matrix = new double[cols][rows];
1309                 for (int row = 0; row < rows; row++)
1310                 {
1311                     for (int col = 0; col < cols; col++)
1312                     {
1313                         if (col == 0)
1314                             matrix[row] = new double[cols];
1315                         matrix[col][row] = mb.getDouble();
1316                     }
1317                 }
1318                 try
1319                 {
1320                     for (int col = 0; col < cols; col++)
1321                     {
1322                         vArray[col] = DoubleVectorUtil.instantiateAnonymousSI(matrix[col], units[col], StorageType.DENSE);
1323                     }
1324                     list.add(vArray);
1325                 }
1326                 catch (ValueException exception)
1327                 {
1328                     throw new Sim0MQException(exception);
1329                 }
1330             }
1331 
1332             else
1333             {
1334                 throw new Sim0MQException("Unknown data type " + type + " in the ZeroMQ message while decoding");
1335             }
1336         }
1337 
1338         Object[] array = list.toArray();
1339         return array;
1340     }
1341 
1342     /**
1343      * Indicate whether extra bytes are needed for a Money per quantity type.
1344      * @param o the object to check
1345      * @return 0 or 1 to indicate whether an extra byte is needed
1346      */
1347     private static int extraBytesMoney(final Object o)
1348     {
1349         if (o instanceof Money)
1350         {
1351             return 1;
1352         }
1353         else if (o instanceof MoneyPerArea || o instanceof MoneyPerEnergy || o instanceof MoneyPerLength
1354                 || o instanceof MoneyPerMass || o instanceof MoneyPerDuration || o instanceof MoneyPerVolume)
1355         {
1356             return 2;
1357         }
1358         else if (o instanceof FloatMoney)
1359         {
1360             return 1;
1361         }
1362         else if (o instanceof FloatMoneyPerArea || o instanceof FloatMoneyPerEnergy || o instanceof FloatMoneyPerLength
1363                 || o instanceof FloatMoneyPerMass || o instanceof FloatMoneyPerDuration || o instanceof FloatMoneyPerVolume)
1364         {
1365             return 2;
1366         }
1367         return 0;
1368     }
1369 
1370     /**
1371      * Return a readable string with the bytes in a byte[] message.
1372      * @param bytes byte[]; the byte array to display
1373      * @return String; a readable string with the bytes in a byte[] message
1374      */
1375     public static String printBytes(final byte[] bytes)
1376     {
1377         StringBuffer s = new StringBuffer();
1378         s.append("|");
1379         for (int b : bytes)
1380         {
1381             if (b < 0)
1382             {
1383                 b += 128;
1384             }
1385             if (b >= 32 && b <= 127)
1386             {
1387                 s.append("#" + Integer.toString(b, 16).toUpperCase() + "(" + (char) (byte) b + ")|");
1388             }
1389             else
1390             {
1391                 s.append("#" + Integer.toString(b, 16).toUpperCase() + "|");
1392             }
1393         }
1394         return s.toString();
1395     }
1396 
1397 }