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