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 public class SerialDataDecoder implements Decoder
24 {
25
26 private final EndianUtil endianUtil;
27
28
29 private byte currentFieldType;
30
31
32 private Serializer<?> currentSerializer = null;
33
34
35 private int positionInData = -1;
36
37
38 private int nextDataElementByte = -1;
39
40
41 private byte[] dataElementBytes = new byte[0];
42
43
44 private int totalDataSize = -1;
45
46
47 private int rowCount;
48
49
50 private int columnCount;
51
52
53 private int currentRow;
54
55
56 private int currentColumn;
57
58
59 private Unit<?> displayUnit;
60
61
62 private StringBuilder buffer = new StringBuilder();
63
64
65
66
67
68 public SerialDataDecoder(final EndianUtil endianUtil)
69 {
70 this.endianUtil = endianUtil;
71 }
72
73 @Override
74 public final String getResult()
75 {
76 String result = this.buffer.toString();
77 this.buffer.setLength(0);
78 return result;
79 }
80
81 @Override
82 public final int getMaximumWidth()
83 {
84 return 80;
85 }
86
87 @Override
88 public final boolean append(final int address, final byte theByte) throws IOException
89 {
90 boolean result = false;
91 if (null == this.currentSerializer)
92 {
93
94 this.currentFieldType = theByte;
95 this.currentSerializer = TypedMessage.PRIMITIVE_DATA_DECODERS.get(this.currentFieldType);
96 if (null == this.currentSerializer)
97 {
98 this.buffer.append(String.format("Bad field type %02x - resynchronizing", this.currentFieldType));
99 result = true;
100
101 }
102 else
103 {
104 this.buffer.append(this.currentSerializer.dataClassName() + (this.currentSerializer.getNumberOfDimensions() > 0
105 || this.currentSerializer.dataClassName().startsWith("Djunits") ? " " : ": "));
106 this.positionInData = 1;
107 this.totalDataSize = 1;
108 this.columnCount = 0;
109 this.rowCount = 0;
110 this.displayUnit = null;
111 if (this.currentSerializer.dataClassName().startsWith("String_"))
112 {
113 prepareForDataElement(4);
114 this.totalDataSize += 4;
115 }
116 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
117 {
118 prepareForDataElement(8);
119 this.totalDataSize += 8;
120 }
121 else if (this.currentSerializer.getNumberOfDimensions() > 0)
122 {
123 int size = this.currentSerializer.getNumberOfDimensions() * 4;
124 prepareForDataElement(size);
125 this.totalDataSize += size;
126 }
127 else if (this.currentSerializer instanceof ObjectSerializer)
128 {
129 try
130 {
131 int size;
132 if (this.currentSerializer.dataClassName().startsWith("Djunits"))
133 {
134
135
136 size = 2;
137 this.displayUnit = null;
138 }
139 else
140 {
141 size = this.currentSerializer.size(null);
142 }
143 prepareForDataElement(size);
144 this.totalDataSize += size;
145 }
146 catch (SerializationException e)
147 {
148 e.printStackTrace();
149 }
150 }
151 else
152 {
153 try
154 {
155 int size = this.currentSerializer.size(null);
156 this.totalDataSize += size;
157 prepareForDataElement(size);
158 }
159 catch (SerializationException e)
160 {
161 e.printStackTrace();
162 }
163 }
164 }
165 return result;
166 }
167 if (this.nextDataElementByte < this.dataElementBytes.length)
168 {
169 this.dataElementBytes[this.nextDataElementByte] = theByte;
170 }
171 this.nextDataElementByte++;
172 this.positionInData++;
173 if (this.nextDataElementByte == this.dataElementBytes.length)
174 {
175 if (this.currentSerializer.dataClassName().startsWith("String_"))
176 {
177 int elementSize = this.currentSerializer.dataClassName().endsWith("8") ? 1 : 2;
178 if (this.columnCount == 0)
179 {
180 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
181 prepareForDataElement(elementSize);
182 this.totalDataSize += this.columnCount * elementSize;
183 }
184 else
185 {
186 if (1 == elementSize)
187 {
188 if (this.dataElementBytes[0] > 32 && this.dataElementBytes[0] < 127)
189 {
190 this.buffer.append((char) this.dataElementBytes[0]);
191 }
192 else
193 {
194 this.buffer.append(".");
195 }
196 }
197 else
198 {
199 char character = this.endianUtil.decodeChar(this.dataElementBytes, 0);
200 if (Character.isAlphabetic(character))
201 {
202 this.buffer.append(character);
203 }
204 else
205 {
206 this.buffer.append(".");
207 }
208 }
209 }
210 this.currentColumn = 0;
211 this.nextDataElementByte = 0;
212 }
213 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
214 {
215 if (this.rowCount == 0)
216 {
217 this.rowCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
218 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
219 this.currentRow = -1;
220 this.currentColumn = 0;
221 prepareForDataElement(2);
222 this.totalDataSize += 2;
223 }
224 else if (this.currentRow < 0)
225 {
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 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("height %d, width %d", this.rowCount, this.columnCount));
357 }
358 else
359 {
360 this.rowCount = 1;
361 this.buffer.append(String.format("length %d", this.columnCount));
362 }
363 int elementSize = -1;
364 if (this.currentSerializer instanceof ArrayOrMatrixSerializer<?, ?>)
365 {
366 elementSize = ((ArrayOrMatrixSerializer<?, ?>) this.currentSerializer).getElementSize();
367 }
368 else if (this.currentSerializer instanceof BasicPrimitiveArrayOrMatrixSerializer)
369 {
370 elementSize = ((BasicPrimitiveArrayOrMatrixSerializer<?>) this.currentSerializer).getElementSize();
371 }
372 else
373 {
374 throw new RuntimeException("Unhandled type of array or matrix serializer");
375 }
376 this.totalDataSize += elementSize * this.rowCount * this.columnCount;
377 prepareForDataElement(elementSize);
378
379
380 }
381 else
382 {
383
384 if (this.currentSerializer.getNumberOfDimensions() == 1)
385 {
386 this.buffer.append(String.format("value at index %d: ", this.currentColumn));
387 }
388 else
389 {
390 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
391 }
392 if (this.currentSerializer instanceof ArrayOrMatrixSerializer<?, ?>)
393 {
394 Object value = ((ArrayOrMatrixSerializer<?, ?>) this.currentSerializer)
395 .deSerializeElement(this.dataElementBytes, 0, this.endianUtil);
396 this.buffer.append(value.toString());
397 }
398 else if (this.currentSerializer instanceof BasicPrimitiveArrayOrMatrixSerializer)
399 {
400
401 BasicPrimitiveArrayOrMatrixSerializer<?> basicPrimitiveArraySerializer =
402 (BasicPrimitiveArrayOrMatrixSerializer<?>) this.currentSerializer;
403 switch (basicPrimitiveArraySerializer.fieldType())
404 {
405 case FieldTypes.BYTE_8_ARRAY:
406 case FieldTypes.BYTE_8_MATRIX:
407 this.buffer.append(String.format("%02x", this.dataElementBytes[0]));
408 break;
409
410 case FieldTypes.SHORT_16_ARRAY:
411 case FieldTypes.SHORT_16_MATRIX:
412 this.buffer.append(String.format("%d", this.endianUtil.decodeShort(this.dataElementBytes, 0)));
413 break;
414
415 case FieldTypes.INT_32_ARRAY:
416 case FieldTypes.INT_32_MATRIX:
417 this.buffer.append(String.format("%d", this.endianUtil.decodeInt(this.dataElementBytes, 0)));
418 break;
419
420 case FieldTypes.LONG_64_ARRAY:
421 case FieldTypes.LONG_64_MATRIX:
422 this.buffer.append(String.format("%d", this.endianUtil.decodeLong(this.dataElementBytes, 0)));
423 break;
424
425 case FieldTypes.FLOAT_32_ARRAY:
426 case FieldTypes.FLOAT_32_MATRIX:
427 this.buffer.append(String.format("%f", this.endianUtil.decodeFloat(this.dataElementBytes, 0)));
428 break;
429
430 case FieldTypes.DOUBLE_64_ARRAY:
431 case FieldTypes.DOUBLE_64_MATRIX:
432 this.buffer.append(String.format("%f", this.endianUtil.decodeDouble(this.dataElementBytes, 0)));
433 break;
434
435 case FieldTypes.BOOLEAN_8_ARRAY:
436 case FieldTypes.BOOLEAN_8_MATRIX:
437 this.buffer.append(0 == this.dataElementBytes[0] ? "false" : "true");
438 break;
439
440 default:
441 throw new RuntimeException(
442 "Unhandled type of basicPrimitiveArraySerializer: " + basicPrimitiveArraySerializer);
443 }
444 }
445 this.nextDataElementByte = 0;
446 this.currentColumn++;
447 if (this.currentColumn == this.columnCount)
448 {
449 this.currentColumn = 0;
450 this.currentRow++;
451 }
452 }
453
454
455 result = true;
456 }
457 }
458 if (this.positionInData == this.totalDataSize)
459
460 {
461 this.currentSerializer = null;
462 this.positionInData = -1;
463 this.totalDataSize = -1;
464 this.rowCount = 0;
465 this.columnCount = 0;
466 return true;
467 }
468 return result;
469 }
470
471
472
473
474
475 private void prepareForDataElement(final int dataElementSize)
476 {
477 this.dataElementBytes = new byte[dataElementSize];
478 this.nextDataElementByte = 0;
479 }
480
481 @Override
482 public final boolean ignoreForIdenticalOutputCheck()
483 {
484 return false;
485 }
486
487 }