1 package org.djutils.serialization;
2
3 import java.io.IOException;
4
5 import org.djunits.unit.Unit;
6 import org.djutils.decoderdumper.Decoder;
7 import org.djutils.serialization.serializers.ArrayOrMatrixSerializer;
8 import org.djutils.serialization.serializers.BasicPrimitiveArrayOrMatrixSerializer;
9 import org.djutils.serialization.serializers.FixedSizeObjectSerializer;
10 import org.djutils.serialization.serializers.ObjectSerializer;
11 import org.djutils.serialization.serializers.Pointer;
12 import org.djutils.serialization.serializers.Serializer;
13
14
15
16
17
18
19
20
21
22
23
24 public class SerialDataDecoder implements Decoder
25 {
26
27 private final EndianUtil endianUtil;
28
29
30 private byte currentFieldType;
31
32
33 private Serializer<?> currentSerializer = null;
34
35
36 private int positionInData = -1;
37
38
39 private int nextDataElementByte = -1;
40
41
42 private byte[] dataElementBytes = new byte[0];
43
44
45 private int totalDataSize = -1;
46
47
48 private int rowCount;
49
50
51 private int columnCount;
52
53
54 private int currentRow;
55
56
57 private int currentColumn;
58
59
60 private Unit<?> displayUnit;
61
62
63 private StringBuilder buffer = new StringBuilder();
64
65
66
67
68
69 public SerialDataDecoder(final EndianUtil endianUtil)
70 {
71 this.endianUtil = endianUtil;
72 }
73
74
75 @Override
76 public final String getResult()
77 {
78 String result = this.buffer.toString();
79 this.buffer.setLength(0);
80 return result;
81 }
82
83
84 @Override
85 public final int getMaximumWidth()
86 {
87 return 80;
88 }
89
90
91 @Override
92 public final boolean append(final int address, final byte theByte) throws IOException
93 {
94 boolean result = false;
95 if (null == this.currentSerializer)
96 {
97
98 this.currentFieldType = theByte;
99 this.currentSerializer = TypedMessage.PRIMITIVE_DATA_DECODERS.get(this.currentFieldType);
100 if (null == this.currentSerializer)
101 {
102 this.buffer.append(String.format("Bad field type %02x - resynchronizing", this.currentFieldType));
103 result = true;
104
105 }
106 else
107 {
108 this.buffer.append(this.currentSerializer.dataClassName() + (this.currentSerializer.getNumberOfDimensions() > 0
109 || this.currentSerializer.dataClassName().startsWith("Djunits") ? " " : ": "));
110 this.positionInData = 1;
111 this.totalDataSize = 1;
112 this.columnCount = 0;
113 this.rowCount = 0;
114 this.displayUnit = null;
115 if (this.currentSerializer.dataClassName().startsWith("String_"))
116 {
117 prepareForDataElement(4);
118 this.totalDataSize += 4;
119 }
120 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
121 {
122 prepareForDataElement(8);
123 this.totalDataSize += 8;
124 }
125 else if (this.currentSerializer.getNumberOfDimensions() > 0)
126 {
127 int size = this.currentSerializer.getNumberOfDimensions() * 4;
128 prepareForDataElement(size);
129 this.totalDataSize += size;
130 }
131 else if (this.currentSerializer instanceof ObjectSerializer)
132 {
133 try
134 {
135 int size;
136 if (this.currentSerializer.dataClassName().startsWith("Djunits"))
137 {
138
139
140 size = 2;
141 this.displayUnit = null;
142 }
143 else
144 {
145 size = this.currentSerializer.size(null);
146 }
147 prepareForDataElement(size);
148 this.totalDataSize += size;
149 }
150 catch (SerializationException e)
151 {
152 e.printStackTrace();
153 }
154 }
155 else
156 {
157 try
158 {
159 int size = this.currentSerializer.size(null);
160 this.totalDataSize += size;
161 prepareForDataElement(size);
162 }
163 catch (SerializationException e)
164 {
165 e.printStackTrace();
166 }
167 }
168 }
169 return result;
170 }
171 if (this.nextDataElementByte < this.dataElementBytes.length)
172 {
173 this.dataElementBytes[this.nextDataElementByte] = theByte;
174 }
175 this.nextDataElementByte++;
176 this.positionInData++;
177 if (this.nextDataElementByte == this.dataElementBytes.length)
178 {
179 if (this.currentSerializer.dataClassName().startsWith("String_"))
180 {
181 int elementSize = this.currentSerializer.dataClassName().endsWith("8") ? 1 : 2;
182 if (this.columnCount == 0)
183 {
184 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
185 prepareForDataElement(elementSize);
186 this.totalDataSize += this.columnCount * elementSize;
187 }
188 else
189 {
190 if (1 == elementSize)
191 {
192 if (this.dataElementBytes[0] > 32 && this.dataElementBytes[0] < 127)
193 {
194 this.buffer.append((char) this.dataElementBytes[0]);
195 }
196 else
197 {
198 this.buffer.append(".");
199 }
200 }
201 else
202 {
203 char character = this.endianUtil.decodeChar(this.dataElementBytes, 0);
204 if (Character.isAlphabetic(character))
205 {
206 this.buffer.append(character);
207 }
208 else
209 {
210 this.buffer.append(".");
211 }
212 }
213 }
214 this.currentColumn = 0;
215 this.nextDataElementByte = 0;
216 }
217 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
218 {
219 if (this.rowCount == 0)
220 {
221 this.rowCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
222 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
223 this.currentRow = -1;
224 this.currentColumn = 0;
225 prepareForDataElement(2);
226 this.totalDataSize += 2;
227 }
228 else if (this.currentRow < 0)
229 {
230
231 TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
232 this.displayUnit = TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
233 this.buffer.append("unit for column " + this.currentColumn + ": ");
234 this.buffer.append(this.displayUnit);
235 this.currentColumn++;
236 if (this.currentColumn < this.columnCount)
237 {
238 prepareForDataElement(2);
239 this.totalDataSize += 2;
240 this.buffer.append(", ");
241 }
242 else
243 {
244
245 this.currentRow = 0;
246 this.currentColumn = 0;
247 prepareForDataElement(8);
248 this.totalDataSize += 8 * this.columnCount * this.rowCount;
249 }
250 }
251 else
252 {
253
254 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
255 this.buffer.append(this.endianUtil.decodeDouble(this.dataElementBytes, 0));
256 this.positionInData = 0;
257 this.currentColumn++;
258 if (this.currentColumn >= this.columnCount)
259 {
260 this.currentColumn = 0;
261 this.currentRow++;
262 }
263 this.buffer.append(" ");
264 this.nextDataElementByte = 0;
265 }
266 }
267 else if (this.currentSerializer.dataClassName().startsWith("Djunits"))
268 {
269 if (this.currentSerializer.getNumberOfDimensions() > 0 && 0 == this.rowCount)
270 {
271 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
272 this.currentRow = 0;
273 this.currentColumn = 0;
274 if (this.dataElementBytes.length == 8)
275 {
276 this.rowCount = this.columnCount;
277 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
278 this.buffer.append(String.format("height %d, width %d", this.rowCount, this.columnCount));
279 }
280 else
281 {
282 this.rowCount = 1;
283 this.buffer.append(String.format("length %d", this.columnCount));
284 }
285
286 prepareForDataElement(2);
287 this.totalDataSize += 2;
288 this.buffer.append(", ");
289 return false;
290 }
291 else if (null == this.displayUnit)
292 {
293 this.displayUnit = TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
294 this.buffer.append("unit " + this.displayUnit);
295 int numberOfDimensions = this.currentSerializer.getNumberOfDimensions();
296 int elementSize = this.currentSerializer.dataClassName().contains("Float") ? 4 : 8;
297 this.totalDataSize += elementSize * (0 == numberOfDimensions ? 1 : this.rowCount * this.columnCount);
298 prepareForDataElement(elementSize);
299 if (0 == numberOfDimensions)
300 {
301 this.buffer.append(": ");
302 }
303 else
304 {
305 result = true;
306 }
307 }
308 else
309 {
310
311 int dimensions = this.currentSerializer.getNumberOfDimensions();
312 if (dimensions == 1)
313 {
314 this.buffer.append(String.format("value at index %d: ", this.currentColumn));
315 }
316 else if (dimensions == 2)
317 {
318 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
319 }
320
321 if (dimensions > 0)
322 {
323 this.currentColumn++;
324 if (this.currentColumn >= this.columnCount)
325 {
326 this.currentColumn = 0;
327 this.currentRow++;
328 }
329 }
330 this.buffer.append(this.dataElementBytes.length == 4 ? this.endianUtil.decodeFloat(this.dataElementBytes, 0)
331 : this.endianUtil.decodeDouble(this.dataElementBytes, 0));
332 this.nextDataElementByte = 0;
333 result = true;
334 }
335 }
336 else if (this.currentSerializer instanceof FixedSizeObjectSerializer)
337 {
338 try
339 {
340 Object value = this.currentSerializer.deSerialize(this.dataElementBytes, new Pointer(), this.endianUtil);
341 this.buffer.append(value.toString());
342 }
343 catch (SerializationException e)
344 {
345 this.buffer.append("Error deserializing data");
346 }
347 }
348 else if (this.currentSerializer.getNumberOfDimensions() > 0)
349 {
350 if (this.rowCount == 0)
351 {
352
353 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
354 this.currentRow = 0;
355 this.currentColumn = 0;
356 if (this.dataElementBytes.length == 8)
357 {
358 this.rowCount = this.columnCount;
359 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
360 this.buffer.append(String.format("height %d, width %d", this.rowCount, this.columnCount));
361 }
362 else
363 {
364 this.rowCount = 1;
365 this.buffer.append(String.format("length %d", this.columnCount));
366 }
367 int elementSize = -1;
368 if (this.currentSerializer instanceof ArrayOrMatrixSerializer<?, ?>)
369 {
370 elementSize = ((ArrayOrMatrixSerializer<?, ?>) this.currentSerializer).getElementSize();
371 }
372 else if (this.currentSerializer instanceof BasicPrimitiveArrayOrMatrixSerializer)
373 {
374 elementSize = ((BasicPrimitiveArrayOrMatrixSerializer<?>) this.currentSerializer).getElementSize();
375 }
376 else
377 {
378 throw new RuntimeException("Unhandled type of array or matrix serializer");
379 }
380 this.totalDataSize += elementSize * this.rowCount * this.columnCount;
381 prepareForDataElement(elementSize);
382
383
384 }
385 else
386 {
387
388 if (this.currentSerializer.getNumberOfDimensions() == 1)
389 {
390 this.buffer.append(String.format("value at index %d: ", this.currentColumn));
391 }
392 else
393 {
394 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
395 }
396 if (this.currentSerializer instanceof ArrayOrMatrixSerializer<?, ?>)
397 {
398 Object value = ((ArrayOrMatrixSerializer<?, ?>) this.currentSerializer)
399 .deSerializeElement(this.dataElementBytes, 0, this.endianUtil);
400 this.buffer.append(value.toString());
401 }
402 else if (this.currentSerializer instanceof BasicPrimitiveArrayOrMatrixSerializer)
403 {
404
405 BasicPrimitiveArrayOrMatrixSerializer<?> basicPrimitiveArraySerializer =
406 (BasicPrimitiveArrayOrMatrixSerializer<?>) this.currentSerializer;
407 switch (basicPrimitiveArraySerializer.fieldType())
408 {
409 case FieldTypes.BYTE_8_ARRAY:
410 case FieldTypes.BYTE_8_MATRIX:
411 this.buffer.append(String.format("%02x", this.dataElementBytes[0]));
412 break;
413
414 case FieldTypes.SHORT_16_ARRAY:
415 case FieldTypes.SHORT_16_MATRIX:
416 this.buffer.append(String.format("%d", this.endianUtil.decodeShort(this.dataElementBytes, 0)));
417 break;
418
419 case FieldTypes.INT_32_ARRAY:
420 case FieldTypes.INT_32_MATRIX:
421 this.buffer.append(String.format("%d", this.endianUtil.decodeInt(this.dataElementBytes, 0)));
422 break;
423
424 case FieldTypes.LONG_64_ARRAY:
425 case FieldTypes.LONG_64_MATRIX:
426 this.buffer.append(String.format("%d", this.endianUtil.decodeLong(this.dataElementBytes, 0)));
427 break;
428
429 case FieldTypes.FLOAT_32_ARRAY:
430 case FieldTypes.FLOAT_32_MATRIX:
431 this.buffer.append(String.format("%f", this.endianUtil.decodeFloat(this.dataElementBytes, 0)));
432 break;
433
434 case FieldTypes.DOUBLE_64_ARRAY:
435 case FieldTypes.DOUBLE_64_MATRIX:
436 this.buffer.append(String.format("%f", this.endianUtil.decodeDouble(this.dataElementBytes, 0)));
437 break;
438
439 case FieldTypes.BOOLEAN_8_ARRAY:
440 case FieldTypes.BOOLEAN_8_MATRIX:
441 this.buffer.append(0 == this.dataElementBytes[0] ? "false" : "true");
442 break;
443
444 default:
445 throw new RuntimeException(
446 "Unhandled type of basicPrimitiveArraySerializer: " + basicPrimitiveArraySerializer);
447 }
448 }
449 this.nextDataElementByte = 0;
450 this.currentColumn++;
451 if (this.currentColumn == this.columnCount)
452 {
453 this.currentColumn = 0;
454 this.currentRow++;
455 }
456 }
457
458
459 result = true;
460 }
461 }
462 if (this.positionInData == this.totalDataSize)
463
464 {
465 this.currentSerializer = null;
466 this.positionInData = -1;
467 this.totalDataSize = -1;
468 this.rowCount = 0;
469 this.columnCount = 0;
470 return true;
471 }
472 return result;
473 }
474
475
476
477
478
479 private void prepareForDataElement(final int dataElementSize)
480 {
481 this.dataElementBytes = new byte[dataElementSize];
482 this.nextDataElementByte = 0;
483 }
484
485
486 @Override
487 public final boolean ignoreForIdenticalOutputCheck()
488 {
489 return false;
490 }
491
492 }