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 @Override
76 public final String getResult()
77 {
78 String result = this.buffer.toString();
79 this.buffer.setLength(0);
80 return result;
81 }
82
83 @Override
84 public final int getMaximumWidth()
85 {
86 return 80;
87 }
88
89 @Override
90 public final boolean append(final int address, final byte theByte) throws IOException
91 {
92 boolean result = false;
93 if (null == this.currentSerializer)
94 {
95
96 this.currentFieldType = theByte;
97 this.currentSerializer = TypedMessage.PRIMITIVE_DATA_DECODERS.get(this.currentFieldType);
98 if (null == this.currentSerializer)
99 {
100 this.buffer.append(String.format("Bad field type %02x - resynchronizing", this.currentFieldType));
101 result = true;
102
103 }
104 else
105 {
106 this.buffer.append(this.currentSerializer.dataClassName() + (this.currentSerializer.getNumberOfDimensions() > 0
107 || this.currentSerializer.dataClassName().startsWith("Djunits") ? " " : ": "));
108 this.positionInData = 1;
109 this.totalDataSize = 1;
110 this.columnCount = 0;
111 this.rowCount = 0;
112 this.displayUnit = null;
113 if (this.currentSerializer.dataClassName().startsWith("String_"))
114 {
115 prepareForDataElement(4);
116 this.totalDataSize += 4;
117 }
118 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
119 {
120 prepareForDataElement(8);
121 this.totalDataSize += 8;
122 }
123 else if (this.currentSerializer.getNumberOfDimensions() > 0)
124 {
125 int size = this.currentSerializer.getNumberOfDimensions() * 4;
126 prepareForDataElement(size);
127 this.totalDataSize += size;
128 }
129 else if (this.currentSerializer instanceof ObjectSerializer)
130 {
131 try
132 {
133 int size;
134 if (this.currentSerializer.dataClassName().startsWith("Djunits"))
135 {
136
137
138 size = 2;
139 this.displayUnit = null;
140 }
141 else
142 {
143 size = this.currentSerializer.size(null);
144 }
145 prepareForDataElement(size);
146 this.totalDataSize += size;
147 }
148 catch (SerializationException e)
149 {
150 e.printStackTrace();
151 }
152 }
153 else
154 {
155 try
156 {
157 int size = this.currentSerializer.size(null);
158 this.totalDataSize += size;
159 prepareForDataElement(size);
160 }
161 catch (SerializationException e)
162 {
163 e.printStackTrace();
164 }
165 }
166 }
167 return result;
168 }
169 if (this.nextDataElementByte < this.dataElementBytes.length)
170 {
171 this.dataElementBytes[this.nextDataElementByte] = theByte;
172 }
173 this.nextDataElementByte++;
174 this.positionInData++;
175 if (this.nextDataElementByte == this.dataElementBytes.length)
176 {
177 if (this.currentSerializer.dataClassName().startsWith("String_"))
178 {
179 int elementSize = this.currentSerializer.dataClassName().endsWith("8") ? 1 : 2;
180 if (this.columnCount == 0)
181 {
182 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
183 prepareForDataElement(elementSize);
184 this.totalDataSize += this.columnCount * elementSize;
185 }
186 else
187 {
188 if (1 == elementSize)
189 {
190 if (this.dataElementBytes[0] > 32 && this.dataElementBytes[0] < 127)
191 {
192 this.buffer.append((char) this.dataElementBytes[0]);
193 }
194 else
195 {
196 this.buffer.append(".");
197 }
198 }
199 else
200 {
201 char character = this.endianUtil.decodeChar(this.dataElementBytes, 0);
202 if (Character.isAlphabetic(character))
203 {
204 this.buffer.append(character);
205 }
206 else
207 {
208 this.buffer.append(".");
209 }
210 }
211 }
212 this.currentColumn = 0;
213 this.nextDataElementByte = 0;
214 }
215 else if (this.currentSerializer.dataClassName().contentEquals("Djunits_vector_array"))
216 {
217 if (this.rowCount == 0)
218 {
219 this.rowCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
220 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
221 this.currentRow = -1;
222 this.currentColumn = 0;
223 prepareForDataElement(2);
224 this.totalDataSize += 2;
225 }
226 else if (this.currentRow < 0)
227 {
228
229 TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
230 this.displayUnit = TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
231 this.buffer.append("unit for column " + this.currentColumn + ": ");
232 this.buffer.append(this.displayUnit);
233 this.currentColumn++;
234 if (this.currentColumn < this.columnCount)
235 {
236 prepareForDataElement(2);
237 this.totalDataSize += 2;
238 this.buffer.append(", ");
239 }
240 else
241 {
242
243 this.currentRow = 0;
244 this.currentColumn = 0;
245 prepareForDataElement(8);
246 this.totalDataSize += 8 * this.columnCount * this.rowCount;
247 }
248 }
249 else
250 {
251
252 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
253 this.buffer.append(this.endianUtil.decodeDouble(this.dataElementBytes, 0));
254 this.positionInData = 0;
255 this.currentColumn++;
256 if (this.currentColumn >= this.columnCount)
257 {
258 this.currentColumn = 0;
259 this.currentRow++;
260 }
261 this.buffer.append(" ");
262 this.nextDataElementByte = 0;
263 }
264 }
265 else if (this.currentSerializer.dataClassName().startsWith("Djunits"))
266 {
267 if (this.currentSerializer.getNumberOfDimensions() > 0 && 0 == this.rowCount)
268 {
269 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
270 this.currentRow = 0;
271 this.currentColumn = 0;
272 if (this.dataElementBytes.length == 8)
273 {
274 this.rowCount = this.columnCount;
275 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
276 this.buffer.append(String.format("height %d, width %d", this.rowCount, this.columnCount));
277 }
278 else
279 {
280 this.rowCount = 1;
281 this.buffer.append(String.format("length %d", this.columnCount));
282 }
283
284 prepareForDataElement(2);
285 this.totalDataSize += 2;
286 this.buffer.append(", ");
287 return false;
288 }
289 else if (null == this.displayUnit)
290 {
291 this.displayUnit = TypedMessage.getUnit(this.dataElementBytes, new Pointer(), this.endianUtil);
292 this.buffer.append("unit " + this.displayUnit);
293 int numberOfDimensions = this.currentSerializer.getNumberOfDimensions();
294 int elementSize = this.currentSerializer.dataClassName().contains("Float") ? 4 : 8;
295 this.totalDataSize += elementSize * (0 == numberOfDimensions ? 1 : this.rowCount * this.columnCount);
296 prepareForDataElement(elementSize);
297 if (0 == numberOfDimensions)
298 {
299 this.buffer.append(": ");
300 }
301 else
302 {
303 result = true;
304 }
305 }
306 else
307 {
308
309 int dimensions = this.currentSerializer.getNumberOfDimensions();
310 if (dimensions == 1)
311 {
312 this.buffer.append(String.format("value at index %d: ", this.currentColumn));
313 }
314 else if (dimensions == 2)
315 {
316 this.buffer.append(String.format("value at row %d column %d: ", this.currentRow, this.currentColumn));
317 }
318
319 if (dimensions > 0)
320 {
321 this.currentColumn++;
322 if (this.currentColumn >= this.columnCount)
323 {
324 this.currentColumn = 0;
325 this.currentRow++;
326 }
327 }
328 this.buffer.append(this.dataElementBytes.length == 4 ? this.endianUtil.decodeFloat(this.dataElementBytes, 0)
329 : this.endianUtil.decodeDouble(this.dataElementBytes, 0));
330 this.nextDataElementByte = 0;
331 result = true;
332 }
333 }
334 else if (this.currentSerializer instanceof FixedSizeObjectSerializer)
335 {
336 try
337 {
338 Object value = this.currentSerializer.deSerialize(this.dataElementBytes, new Pointer(), this.endianUtil);
339 this.buffer.append(value.toString());
340 }
341 catch (SerializationException e)
342 {
343 this.buffer.append("Error deserializing data");
344 }
345 }
346 else if (this.currentSerializer.getNumberOfDimensions() > 0)
347 {
348 if (this.rowCount == 0)
349 {
350
351 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
352 this.currentRow = 0;
353 this.currentColumn = 0;
354 if (this.dataElementBytes.length == 8)
355 {
356 this.rowCount = this.columnCount;
357 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
358 this.buffer.append(String.format("height %d, width %d", this.rowCount, this.columnCount));
359 }
360 else
361 {
362 this.rowCount = 1;
363 this.buffer.append(String.format("length %d", 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 private void prepareForDataElement(final int dataElementSize)
478 {
479 this.dataElementBytes = new byte[dataElementSize];
480 this.nextDataElementByte = 0;
481 }
482
483 @Override
484 public final boolean ignoreForIdenticalOutputCheck()
485 {
486 return false;
487 }
488
489 }