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 80;
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() + (this.currentSerializer.getNumberOfDimensions() > 0
101 || this.currentSerializer.dataClassName().startsWith("Djunits") ? " " : ": "));
102 this.positionInData = 1;
103 this.totalDataSize = 1;
104 this.columnCount = 0;
105 this.rowCount = 0;
106 this.displayUnit = null;
107 if (this.currentSerializer.dataClassName().startsWith("String_"))
108 {
109 prepareForDataElement(4);
110 this.totalDataSize += 4;
111 }
112 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
113 {
114 prepareForDataElement(8);
115 this.totalDataSize += 8;
116 }
117 else if (this.currentSerializer.getNumberOfDimensions() > 0)
118 {
119 int size = this.currentSerializer.getNumberOfDimensions() * 4;
120 prepareForDataElement(size);
121 this.totalDataSize += size;
122 }
123 else if (this.currentSerializer instanceof ObjectSerializer)
124 {
125 try
126 {
127 int size;
128 if (this.currentSerializer.dataClassName().startsWith("Djunits"))
129 {
130
131
132 size = 2;
133 this.displayUnit = null;
134 }
135 else
136 {
137 size = this.currentSerializer.size(null);
138 }
139 prepareForDataElement(size);
140 this.totalDataSize += size;
141 }
142 catch (SerializationException e)
143 {
144 e.printStackTrace();
145 }
146 }
147 else
148 {
149 try
150 {
151 int size = this.currentSerializer.size(null);
152 this.totalDataSize += size;
153 prepareForDataElement(size);
154 }
155 catch (SerializationException e)
156 {
157 e.printStackTrace();
158 }
159 }
160 }
161 return result;
162 }
163 if (this.nextDataElementByte < this.dataElementBytes.length)
164 {
165 this.dataElementBytes[this.nextDataElementByte] = theByte;
166 }
167 this.nextDataElementByte++;
168 this.positionInData++;
169 if (this.nextDataElementByte == this.dataElementBytes.length)
170 {
171 if (this.currentSerializer.dataClassName().startsWith("String_"))
172 {
173 int elementSize = this.currentSerializer.dataClassName().endsWith("8") ? 1 : 2;
174 if (this.columnCount == 0)
175 {
176 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
177 prepareForDataElement(elementSize);
178 this.totalDataSize += this.columnCount * elementSize;
179 }
180 else
181 {
182 if (1 == elementSize)
183 {
184 if (this.dataElementBytes[0] > 32 && this.dataElementBytes[0] < 127)
185 {
186 this.buffer.append((char) this.dataElementBytes[0]);
187 }
188 else
189 {
190 this.buffer.append(".");
191 }
192 }
193 else
194 {
195 char character = this.endianUtil.decodeChar(this.dataElementBytes, 0);
196 if (Character.isAlphabetic(character))
197 {
198 this.buffer.append(character);
199 }
200 else
201 {
202 this.buffer.append(".");
203 }
204 }
205 }
206 this.currentColumn = 0;
207 this.nextDataElementByte = 0;
208 }
209 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
210 {
211 if (this.rowCount == 0)
212 {
213 this.rowCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
214 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
215 this.currentRow = -1;
216 this.currentColumn = 0;
217 prepareForDataElement(2);
218 this.totalDataSize += 2;
219 }
220 else if (this.currentRow < 0)
221 {
222
223 if (checkMoneyNeedsMoreBytes())
224 {
225 return false;
226 }
227 TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
228 this.displayUnit = TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
229 this.buffer.append("unit for column " + this.currentColumn + ": ");
230 this.buffer.append(this.displayUnit);
231 this.currentColumn++;
232 if (this.currentColumn < this.columnCount)
233 {
234 prepareForDataElement(2);
235 this.totalDataSize += 2;
236 this.buffer.append(", ");
237 }
238 else
239 {
240
241 this.currentRow = 0;
242 this.currentColumn = 0;
243 prepareForDataElement(8);
244 this.totalDataSize += 8 * this.columnCount * this.rowCount;
245 }
246 }
247 else
248 {
249
250 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
251 this.buffer.append(this.endianUtil.decodeDouble(this.dataElementBytes, 0));
252 this.positionInData = 0;
253 this.currentColumn++;
254 if (this.currentColumn >= this.columnCount)
255 {
256 this.currentColumn = 0;
257 this.currentRow++;
258 }
259 this.buffer.append(" ");
260 this.nextDataElementByte = 0;
261 }
262 }
263 else if (this.currentSerializer.dataClassName().startsWith("Djunits"))
264 {
265 if (this.currentSerializer.getNumberOfDimensions() > 0 && 0 == this.rowCount)
266 {
267 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
268 this.currentRow = 0;
269 this.currentColumn = 0;
270 if (this.dataElementBytes.length == 8)
271 {
272 this.rowCount = this.columnCount;
273 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
274 this.buffer.append(String.format("height %d, width %d", this.rowCount, this.columnCount));
275 }
276 else
277 {
278 this.rowCount = 1;
279 this.buffer.append(String.format("length %d", this.columnCount));
280 }
281
282 prepareForDataElement(2);
283 this.totalDataSize += 2;
284 this.buffer.append(", ");
285 return false;
286 }
287 else if (null == this.displayUnit)
288 {
289 if (checkMoneyNeedsMoreBytes())
290 {
291 return false;
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
480 private boolean checkMoneyNeedsMoreBytes()
481 {
482 if (this.dataElementBytes[0] < 100 || this.dataElementBytes[0] > 106 || this.dataElementBytes.length != 2)
483 {
484 return false;
485 }
486 int requiredLength = this.dataElementBytes[0] == 100 ? 3 : 4;
487 this.totalDataSize += requiredLength - this.dataElementBytes.length;
488 this.dataElementBytes = java.util.Arrays.copyOf(this.dataElementBytes, requiredLength);
489 return true;
490 }
491
492
493
494
495
496 private void prepareForDataElement(final int dataElementSize)
497 {
498 this.dataElementBytes = new byte[dataElementSize];
499 this.nextDataElementByte = 0;
500 }
501
502 @Override
503 public final boolean ignoreForIdenticalOutputCheck()
504 {
505 return false;
506 }
507
508 }