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