+package org.simantics.databoard.streaming;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.type.ArrayType;
+import org.simantics.databoard.type.BooleanType;
+import org.simantics.databoard.type.ByteType;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.Datatype.Visitor;
+import org.simantics.databoard.type.DoubleType;
+import org.simantics.databoard.type.FloatType;
+import org.simantics.databoard.type.IntegerType;
+import org.simantics.databoard.type.LongType;
+import org.simantics.databoard.type.MapType;
+import org.simantics.databoard.type.OptionalType;
+import org.simantics.databoard.type.RecordType;
+import org.simantics.databoard.type.StringType;
+import org.simantics.databoard.type.UnionType;
+import org.simantics.databoard.type.VariantType;
+import org.simantics.databoard.util.Range;
+import org.simantics.databoard.util.binary.Endian;
+
+/**
+ * Utility for debugging corrupted values.
+ */
+public class DataSerializationDebugger {
+ DataReader in;
+ PrintStream out;
+ int indentation;
+
+ private void indentation() {
+ for(int i=0;i<indentation;++i)
+ out.print(" ");
+ }
+
+ /**
+ * Constructs the debugger that reads the given stream and prints the value
+ * content to given print stream.
+ */
+ public DataSerializationDebugger(DataReader in, PrintStream out) {
+ this.in = in;
+ this.out = out;
+ }
+
+ /**
+ * Expects value of given datatype in the input stream.
+ */
+ public void expect(Datatype datatype) throws IOException {
+ if(datatype instanceof BooleanType)
+ expectBoolean();
+ else if(datatype instanceof ByteType)
+ expectByte();
+ else if(datatype instanceof IntegerType)
+ expectInteger();
+ else if(datatype instanceof LongType)
+ expectLong();
+ else if(datatype instanceof FloatType)
+ expectFloat();
+ else if(datatype instanceof DoubleType)
+ expectDouble();
+ else if(datatype instanceof StringType)
+ expectString();
+ else if(datatype instanceof RecordType)
+ expectRecord((RecordType)datatype);
+ else if(datatype instanceof ArrayType)
+ expectArray((ArrayType)datatype);
+ else if(datatype instanceof MapType)
+ expectMap((MapType)datatype);
+ else if(datatype instanceof OptionalType)
+ expectOptional((OptionalType)datatype);
+ else if(datatype instanceof UnionType)
+ expectUnion((UnionType)datatype);
+ else if(datatype instanceof VariantType)
+ expectVariant();
+ }
+
+ private void expectVariant() throws IOException {
+ out.print("Variant:");
+ Datatype datatype = in.readDatatype();
+ out.println(" " + datatype);
+ ++indentation;
+ indentation();
+ expect(datatype);
+ --indentation;
+ }
+
+ private void expectUnion(UnionType datatype) throws IOException {
+ out.print("Union");
+ int tag = in.readUnionTag(datatype.getComponentCount());
+ Component component = datatype.getComponent(tag);
+ out.print("(" + component.name + "): ");
+ expect(component.type);
+ }
+
+ private void expectOptional(OptionalType datatype) throws IOException {
+ out.print("Optional: ");
+ if(in.beginOptional()) {
+ expect(datatype.componentType);
+ }
+ else
+ out.println("null");
+ }
+
+ private void expectMap(MapType datatype) throws IOException {
+ out.print("Map");
+ int length = in.beginMap();
+ out.println("(" + length + "):");
+ ++indentation;
+ for(int i=0;i<length;++i) {
+ indentation();
+ out.print(i+"-key: ");
+ expect(datatype.keyType);
+ indentation();
+ out.print(i+"-value: ");
+ expect(datatype.valueType);
+ }
+ --indentation;
+ }
+
+ private static final Integer I1 = 1;
+ private static final Integer I4 = 4;
+ private static final Integer I8 = 8;
+
+ private static int getFixedLength(ArrayType b) {
+ Range length = b.getLength();
+ return length.getLower().getValue().intValue();
+ }
+
+ private static Visitor<Integer> FIXED_SIZE = new Visitor<Integer>() {
+
+ @Override
+ public Integer visit(ArrayType b) {
+ Range length = b.getLength();
+ if(length == null
+ || length.getLower().getValue()==null
+ || !length.getLower().equals(length.getUpper()))
+ return null;
+
+ int fixedLength = length.getLower().getValue().intValue();
+
+ Integer componentLength = b.componentType.accept(this);
+ if(componentLength == null)
+ return null;
+
+ return fixedLength * componentLength;
+ }
+
+ @Override
+ public Integer visit(BooleanType b) {
+ return I1;
+ }
+
+ @Override
+ public Integer visit(DoubleType b) {
+ return I8;
+ }
+
+ @Override
+ public Integer visit(FloatType b) {
+ return I4;
+ }
+
+ @Override
+ public Integer visit(IntegerType b) {
+ return I4;
+ }
+
+ @Override
+ public Integer visit(ByteType b) {
+ return I1;
+ }
+
+ @Override
+ public Integer visit(LongType b) {
+ return I8;
+ }
+
+ @Override
+ public Integer visit(OptionalType b) {
+ return null;
+ }
+
+ @Override
+ public Integer visit(RecordType b) {
+ if(b.isReferable())
+ return null;
+ int sum = 0;
+ for(Component component : b.getComponents()) {
+ Integer componentSize = component.type.accept(this);
+ if(componentSize == null)
+ return null;
+ sum += componentSize;
+ }
+ return sum;
+ }
+
+ @Override
+ public Integer visit(StringType b) {
+ return null;
+ }
+
+ @Override
+ public Integer visit(UnionType b) {
+ Integer commonComponentSize = null;
+ for(Component component : b.components) {
+ Integer componentSize = component.type.accept(this);
+ if(componentSize == null)
+ return null;
+ if(commonComponentSize == null)
+ commonComponentSize = componentSize;
+ else if(!commonComponentSize.equals(componentSize))
+ return null;
+ }
+ if(commonComponentSize == null)
+ return 0;
+ return commonComponentSize + Endian.getUIntLength(b.components.length-1);
+ }
+
+ @Override
+ public Integer visit(VariantType b) {
+ return null;
+ }
+
+ @Override
+ public Integer visit(MapType b) {
+ return null;
+ }
+
+ };
+
+ private void expectArray(ArrayType datatype) throws IOException {
+ out.print("Array");
+ int length;
+ if(datatype.accept(FIXED_SIZE) == null)
+ length = in.beginVariableLengthArray();
+ else
+ length = getFixedLength(datatype);
+ out.println("(" + length + "):");
+ ++indentation;
+ for(int i=0;i<length;++i) {
+ indentation();
+ out.print(i);
+ out.print(": ");
+ expect(datatype.componentType);
+ }
+ --indentation;
+ }
+
+ private void expectRecord(RecordType datatype) throws IOException {
+ out.print("Record:");
+ if(datatype.isReferable()) {
+ int ref = in.readReferableRecordReference();
+ if(ref != 0) {
+ out.println(" old " + ref);
+ return;
+ }
+ else {
+ out.println(" new");
+ }
+ }
+ else
+ out.println();
+ ++indentation;
+ for(Component component : datatype.getComponents()) {
+ indentation();
+ out.print(component.name);
+ out.print(": ");
+ expect(component.type);
+ }
+ --indentation;
+ }
+
+ private void expectString() throws IOException {
+ out.print("String");
+ int length = in.readStringLength();
+ out.print("(" + length + "): ");
+ out.println(in.readStringContent(length));
+ }
+
+ private void expectDouble() throws IOException {
+ out.print("Double: ");
+ out.println(in.readDouble());
+ }
+
+ private void expectFloat() throws IOException {
+ out.print("Float: ");
+ out.println(in.readFloat());
+ }
+
+ private void expectLong() throws IOException {
+ out.print("Long: ");
+ out.println(in.readLong());
+ }
+
+ private void expectInteger() throws IOException {
+ out.print("Integer: ");
+ out.println(in.readInteger());
+ }
+
+ private void expectByte() throws IOException {
+ out.print("Byte: ");
+ out.println(in.readByte());
+ }
+
+ private void expectBoolean() throws IOException {
+ out.print("Boolean: ");
+ out.println(in.readBoolean());
+ }
+}