From 87d69cdff34c45de3e329f13db67d892975b8451 Mon Sep 17 00:00:00 2001 From: Jean Meurice Date: Mon, 13 Jul 2020 16:41:38 +0100 Subject: [PATCH] Automated Json --- pom.xml | 2 +- .../montisim/commons/boundingbox/AABB.java | 31 +- .../commons/boundingbox/BoundingBox.java | 39 +- .../montisim/commons/boundingbox/OBB.java | 86 +- .../montisim/commons/boundingbox/Sphere.java | 23 +- .../commons/dynamicinterface/ArrayType.java | 148 +-- .../commons/dynamicinterface/DataType.java | 144 +- .../commons/simulation/DynamicObject.java | 49 +- .../commons/simulation/ISimulator.java | 10 +- .../commons/simulation/ISimulatorState.java | 5 + .../commons/simulation/PhysicalValue.java | 50 +- .../commons/simulation/SimulationObject.java | 6 +- .../commons/simulation/StaticObject.java | 68 +- .../montisim/commons/simulation/Updater.java | 2 +- .../commons/utils/JsonSerializable.java | 6 - .../de/rwth/montisim/commons/utils/Mat2.java | 47 +- .../de/rwth/montisim/commons/utils/Mat3.java | 51 +- .../de/rwth/montisim/commons/utils/Mat4.java | 30 +- .../de/rwth/montisim/commons/utils/Time.java | 21 - .../de/rwth/montisim/commons/utils/Vec2.java | 46 +- .../de/rwth/montisim/commons/utils/Vec3.java | 51 +- .../de/rwth/montisim/commons/utils/Vec4.java | 55 +- .../commons/utils/json/CustomJson.java | 10 + .../commons/utils/json/FieldOptional.java | 9 +- .../commons/utils/json/FieldSelect.java | 9 +- .../montisim/commons/utils/json/Json.java | 1167 ++++++++++++----- .../utils/{ => json}/JsonTraverser.java | 38 +- .../json/{Subtyped.java => JsonType.java} | 4 +- .../commons/utils/json/JsonTyped.java | 9 - .../commons/utils/{ => json}/JsonWriter.java | 157 +-- .../montisim/commons/utils/json/Jsonable.java | 11 - .../montisim/commons/utils/json/Select.java | 6 + .../utils/json/SerializationContext.java | 5 + .../json/{StructureType.java => Type.java} | 2 +- .../commons/utils/json/AnnotatedJsonTest.java | 741 +++++++++-- .../commons/utils/{ => json}/JsonTest.java | 8 +- 36 files changed, 1982 insertions(+), 1164 deletions(-) create mode 100644 src/main/java/de/rwth/montisim/commons/simulation/ISimulatorState.java delete mode 100644 src/main/java/de/rwth/montisim/commons/utils/JsonSerializable.java create mode 100644 src/main/java/de/rwth/montisim/commons/utils/json/CustomJson.java rename src/main/java/de/rwth/montisim/commons/utils/{ => json}/JsonTraverser.java (92%) rename src/main/java/de/rwth/montisim/commons/utils/json/{Subtyped.java => JsonType.java} (67%) delete mode 100644 src/main/java/de/rwth/montisim/commons/utils/json/JsonTyped.java rename src/main/java/de/rwth/montisim/commons/utils/{ => json}/JsonWriter.java (96%) delete mode 100644 src/main/java/de/rwth/montisim/commons/utils/json/Jsonable.java create mode 100644 src/main/java/de/rwth/montisim/commons/utils/json/Select.java create mode 100644 src/main/java/de/rwth/montisim/commons/utils/json/SerializationContext.java rename src/main/java/de/rwth/montisim/commons/utils/json/{StructureType.java => Type.java} (70%) rename src/test/java/de/rwth/montisim/commons/utils/{ => json}/JsonTest.java (97%) diff --git a/pom.xml b/pom.xml index f572436..6c007ad 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ montisim commons - 2.0.2 + 2.0.3 diff --git a/src/main/java/de/rwth/montisim/commons/boundingbox/AABB.java b/src/main/java/de/rwth/montisim/commons/boundingbox/AABB.java index f4a6e53..ab016f1 100644 --- a/src/main/java/de/rwth/montisim/commons/boundingbox/AABB.java +++ b/src/main/java/de/rwth/montisim/commons/boundingbox/AABB.java @@ -1,29 +1,28 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.boundingbox; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonTraverser.ObjectIterable; -import de.rwth.montisim.commons.utils.JsonWriter; +import de.rwth.montisim.commons.utils.json.Typed; +@Typed("aabb") public class AABB implements BoundingBox { - public static final String TYPE_NAME = "aabb"; + // public static final String TYPE_NAME = "aabb"; - @Override - public void toJson(JsonWriter j) { - // TODO Auto-generated method stub + // @Override + // public void toJson(JsonWriter j) { + // // TODO Auto-generated method stub - } + // } - @Override - public void fromJson(JsonTraverser j) { - // TODO Auto-generated method stub + // @Override + // public void fromJson(JsonTraverser j) { + // // TODO Auto-generated method stub - } + // } - @Override - public void fromJson(JsonTraverser j, ObjectIterable it) { - // TODO Auto-generated method stub + // @Override + // public void fromJson(JsonTraverser j, ObjectIterable it) { + // // TODO Auto-generated method stub - } + // } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/boundingbox/BoundingBox.java b/src/main/java/de/rwth/montisim/commons/boundingbox/BoundingBox.java index ea8eb7b..01ac3f8 100644 --- a/src/main/java/de/rwth/montisim/commons/boundingbox/BoundingBox.java +++ b/src/main/java/de/rwth/montisim/commons/boundingbox/BoundingBox.java @@ -1,28 +1,23 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.boundingbox; -import de.rwth.montisim.commons.utils.JsonSerializable; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.Pair; -import de.rwth.montisim.commons.utils.JsonTraverser.ObjectIterable; +public interface BoundingBox { + // void fromJson(JsonTraverser j, ObjectIterable it); -public interface BoundingBox extends JsonSerializable { - void fromJson(JsonTraverser j, ObjectIterable it); + // public static BoundingBox buildFromJson(JsonTraverser j){ + // Pair p = j.getStructureType(); + // BoundingBox bbox = getBoundingBoxFromType(p.getKey()); + // bbox.fromJson(j, p.getValue()); + // return bbox; + // } - public static BoundingBox buildFromJson(JsonTraverser j){ - Pair p = j.getStructureType(); - BoundingBox bbox = getBoundingBoxFromType(p.getKey()); - bbox.fromJson(j, p.getValue()); - return bbox; - } - - public static BoundingBox getBoundingBoxFromType(String type){ - if (type.equals(OBB.TYPE_NAME)){ - return new OBB(); - } else if (type.equals(AABB.TYPE_NAME)){ - return new AABB(); - } else if (type.equals(Sphere.TYPE_NAME)){ - return new Sphere(); - } else throw new IllegalArgumentException("Unknown BoundingBox type: "+type); - } + // public static BoundingBox getBoundingBoxFromType(String type){ + // if (type.equals(OBB.TYPE_NAME)){ + // return new OBB(); + // } else if (type.equals(AABB.TYPE_NAME)){ + // return new AABB(); + // } else if (type.equals(Sphere.TYPE_NAME)){ + // return new Sphere(); + // } else throw new IllegalArgumentException("Unknown BoundingBox type: "+type); + // } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/boundingbox/OBB.java b/src/main/java/de/rwth/montisim/commons/boundingbox/OBB.java index 13f511a..9ed7773 100644 --- a/src/main/java/de/rwth/montisim/commons/boundingbox/OBB.java +++ b/src/main/java/de/rwth/montisim/commons/boundingbox/OBB.java @@ -1,60 +1,58 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.boundingbox; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonWriter; import de.rwth.montisim.commons.utils.Mat3; import de.rwth.montisim.commons.utils.Vec3; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; -import de.rwth.montisim.commons.utils.JsonTraverser.ObjectIterable; +import de.rwth.montisim.commons.utils.json.Typed; //@JsonSubtype("obb") or @JsonTyped("obb") ? //@JsonObject +@Typed("obb") public class OBB implements BoundingBox { - public static final String TYPE_NAME = "obb"; + //public static final String TYPE_NAME = "obb"; public Vec3 offset = new Vec3(); // Offset from center of mass public Vec3 half_extent = new Vec3(); public Mat3 axes = new Mat3(); - @Override - public void toJson(JsonWriter j) { - j.startObject(); - j.write("type", TYPE_NAME); - j.writeKey("offset"); - offset.toJson(j); - j.writeKey("half_extent"); - half_extent.toJson(j); - j.writeKey("axes"); - axes.toJson(j); - j.endObject(); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startObject(); + // j.write("type", TYPE_NAME); + // j.writeKey("offset"); + // offset.toJson(j); + // j.writeKey("half_extent"); + // half_extent.toJson(j); + // j.writeKey("axes"); + // axes.toJson(j); + // j.endObject(); + // } - @Override - public void fromJson(JsonTraverser j) { - fromJson(j, j.streamObject()); - } + // @Override + // public void fromJson(JsonTraverser j) { + // fromJson(j, j.streamObject()); + // } - @Override - public void fromJson(JsonTraverser j, ObjectIterable it) { - boolean a = false, b = false, c = false; - for (Entry e : it) { - if (e.key.equals("offset")) { - offset.fromJson(j); - a = true; - } else if (e.key.equals("half_extent")) { - half_extent.fromJson(j); - b = true; - } else if (e.key.equals("axes")) { - axes.fromJson(j); - c = true; - } else - j.unexpected(e); - } - if (!a) - j.expected("offset"); - if (!b) - j.expected("half_extent"); - if (!c) - j.expected("axes"); - } + // @Override + // public void fromJson(JsonTraverser j, ObjectIterable it) { + // boolean a = false, b = false, c = false; + // for (Entry e : it) { + // if (e.key.equals("offset")) { + // offset.fromJson(j); + // a = true; + // } else if (e.key.equals("half_extent")) { + // half_extent.fromJson(j); + // b = true; + // } else if (e.key.equals("axes")) { + // axes.fromJson(j); + // c = true; + // } else + // j.unexpected(e); + // } + // if (!a) + // j.expected("offset"); + // if (!b) + // j.expected("half_extent"); + // if (!c) + // j.expected("axes"); + // } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/boundingbox/Sphere.java b/src/main/java/de/rwth/montisim/commons/boundingbox/Sphere.java index 806e973..86998f8 100644 --- a/src/main/java/de/rwth/montisim/commons/boundingbox/Sphere.java +++ b/src/main/java/de/rwth/montisim/commons/boundingbox/Sphere.java @@ -1,29 +1,10 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.boundingbox; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonTraverser.ObjectIterable; -import de.rwth.montisim.commons.utils.JsonWriter; +import de.rwth.montisim.commons.utils.json.Typed; +@Typed("sphere") public class Sphere implements BoundingBox { public static final String TYPE_NAME = "sphere"; - @Override - public void toJson(JsonWriter j) { - // TODO Auto-generated method stub - - } - - @Override - public void fromJson(JsonTraverser j) { - // TODO Auto-generated method stub - - } - - @Override - public void fromJson(JsonTraverser j, ObjectIterable it) { - // TODO Auto-generated method stub - - } - } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/dynamicinterface/ArrayType.java b/src/main/java/de/rwth/montisim/commons/dynamicinterface/ArrayType.java index 5f399d2..166d031 100644 --- a/src/main/java/de/rwth/montisim/commons/dynamicinterface/ArrayType.java +++ b/src/main/java/de/rwth/montisim/commons/dynamicinterface/ArrayType.java @@ -1,26 +1,25 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.dynamicinterface; -import java.util.Vector; - -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonWriter; -import de.rwth.montisim.commons.utils.Vec2; -import de.rwth.montisim.commons.utils.Vec3; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import java.lang.reflect.InvocationTargetException; +import de.rwth.montisim.commons.utils.json.Json; +import de.rwth.montisim.commons.utils.json.JsonTraverser; +import de.rwth.montisim.commons.utils.json.SerializationContext; public class ArrayType extends DataType { public static enum Dimensionality { - ARRAY, - DYNAMIC + ARRAY, DYNAMIC } + public DataType baseType; public Dimensionality dimension; /** Number of elems for "ARRAY", max size for "DYNAMIC". */ - public int sizeOrMaxSize; + public int sizeOrMaxSize; - public ArrayType(DataType baseType, Dimensionality dim, int sizeOrMaxSize){ - //super(DataType.Type.ARRAY, baseType.getDataSize() * sizeOrMaxSize); //Must give dataSize manually since the default super() calls getDataSize() which is invalid before setting "baseType" & "sizeOrMaxSize". + public ArrayType(DataType baseType, Dimensionality dim, int sizeOrMaxSize) { + // super(DataType.Type.ARRAY, baseType.getDataSize() * sizeOrMaxSize); //Must + // give dataSize manually since the default super() calls getDataSize() which is + // invalid before setting "baseType" & "sizeOrMaxSize". super(DataType.Type.ARRAY); this.baseType = baseType; this.dimension = dim; @@ -28,17 +27,20 @@ public class ArrayType extends DataType { } @Override - public int getDataSize(){ + public int getDataSize() { return baseType.getDataSize() * sizeOrMaxSize; } @Override - public String toString(){ - if (dimension == Dimensionality.ARRAY) return "["+baseType.toString()+"; "+sizeOrMaxSize+"]"; - else return "<"+baseType.toString()+"; "+sizeOrMaxSize+">"; + public String toString() { + if (dimension == Dimensionality.ARRAY) + return "[" + baseType.toString() + "; " + sizeOrMaxSize + "]"; + else + return "<" + baseType.toString() + "; " + sizeOrMaxSize + ">"; } - // Implement hashCode & equals to be able to perform hashmap lookup by type & type comparison + // Implement hashCode & equals to be able to perform hashmap lookup by type & + // type comparison @Override public int hashCode() { @@ -51,118 +53,22 @@ public class ArrayType extends DataType { } @Override - public boolean equals(Object o){ + public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (this.getClass() != o.getClass()) return false; - ArrayType a = ((ArrayType)o); - return this.baseType.equals(a.baseType) && this.dimension.equals(a.dimension) && this.sizeOrMaxSize == a.sizeOrMaxSize; - } - - @Override - public void toJson(JsonWriter j, Object o){ - j.startArray(); - if (baseType.type == Type.DOUBLE) { - double[] a = (double[])o; - for (double d : a) j.writeValue(d); - } else if (baseType.type == Type.FLOAT) { - float[] a = (float[])o; - for (double f : a) j.writeValue(f); - } else if (baseType.type == Type.INT) { - int[] a = (int[])o; - for (int i : a) j.writeValue(i); - } else if (baseType.type == Type.BYTE) { - byte[] a = (byte[])o; - for (byte b : a) j.writeValue(b); - } else if (baseType.type == Type.BOOLEAN) { - boolean[] a = (boolean[])o; - for (boolean b : a) j.writeValue(b); - } else if (baseType.type == Type.EMPTY) { - - } else if (baseType.type == Type.VEC2) { - Vec2[] a = (Vec2[])o; - for (Vec2 v : a) v.toJson(j); - } else if (baseType.type == Type.VEC3) { - Vec3[] a = (Vec3[])o; - for (Vec3 v : a) v.toJson(j); - } else throw new IllegalArgumentException("Missing array type implementation (type: "+type+")"); - j.endArray(); + ArrayType a = ((ArrayType) o); + return this.baseType.equals(a.baseType) && this.dimension.equals(a.dimension) + && this.sizeOrMaxSize == a.sizeOrMaxSize; } @Override - public Object fromJson(JsonTraverser j){ - if (baseType.type == Type.DOUBLE) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - v.add(j.getDouble()); - } - double[] a = new double[v.size()]; - int i = 0; - for (double d : v) a[i++] = d; - return a; - } else if (baseType.type == Type.FLOAT) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - v.add((float)j.getDouble()); - } - float[] a = new float[v.size()]; - int i = 0; - for (float f : v) a[i++] = f; - return a; - } else if (baseType.type == Type.INT) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - v.add((int)j.getLong()); - } - int[] a = new int[v.size()]; - int i = 0; - for (int f : v) a[i++] = f; - return a; - } else if (baseType.type == Type.BYTE) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - v.add((byte)j.getLong()); - } - byte[] a = new byte[v.size()]; - int i = 0; - for (byte b : v) a[i++] = b; - return a; - } else if (baseType.type == Type.BOOLEAN) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - v.add(j.getBoolean()); - } - boolean[] a = new boolean[v.size()]; - int i = 0; - for (boolean b : v) a[i++] = b; - return a; - } else if (baseType.type == Type.EMPTY) { - return null; - } else if (baseType.type == Type.VEC2) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - Vec2 vec = new Vec2(); - vec.fromJson(j); - v.add(vec); - } - Vec2[] a = new Vec2[v.size()]; - int i = 0; - for (Vec2 b : v) a[i++] = b; - return a; - } else if (baseType.type == Type.VEC3) { - Vector v = new Vector<>(); - for (ValueType t : j.streamArray()){ - Vec3 vec = new Vec3(); - vec.fromJson(j); - v.add(vec); - } - Vec3[] a = new Vec3[v.size()]; - int i = 0; - for (Vec3 b : v) a[i++] = b; - return a; - } else throw new IllegalArgumentException("Missing array type implementation (type: "+type+")"); + public Object fromJson(JsonTraverser j, SerializationContext context) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + if (baseType.type.arrayC == null) return null; + return Json.instantiateFromJson(j, baseType.type.arrayC, context); } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/dynamicinterface/DataType.java b/src/main/java/de/rwth/montisim/commons/dynamicinterface/DataType.java index 4432d70..1f56f21 100644 --- a/src/main/java/de/rwth/montisim/commons/dynamicinterface/DataType.java +++ b/src/main/java/de/rwth/montisim/commons/dynamicinterface/DataType.java @@ -1,87 +1,77 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.dynamicinterface; +import java.lang.reflect.InvocationTargetException; + import de.rwth.montisim.commons.utils.*; +import de.rwth.montisim.commons.utils.json.Json; import de.rwth.montisim.commons.utils.json.JsonEntry; +import de.rwth.montisim.commons.utils.json.JsonTraverser; +import de.rwth.montisim.commons.utils.json.JsonWriter; +import de.rwth.montisim.commons.utils.json.SerializationContext; /** * Reflection class for the types of Messages sent in the simulation. */ public class DataType { - public static final DataType DOUBLE = new DataType(Type.DOUBLE); - public static final DataType FLOAT = new DataType(Type.FLOAT); - public static final DataType INT = new DataType(Type.INT); - public static final DataType BOOLEAN = new DataType(Type.BOOLEAN); - public static final DataType BYTE = new DataType(Type.BYTE); - public static final DataType EMPTY = new DataType(Type.EMPTY); - - public static final DataType VEC2 = new DataType(Type.VEC2); - public static final DataType VEC3 = new DataType(Type.VEC3); + public static final DataType DOUBLE = new DataType(Type.DOUBLE); + public static final DataType FLOAT = new DataType(Type.FLOAT); + public static final DataType INT = new DataType(Type.INT); + public static final DataType BOOLEAN = new DataType(Type.BOOLEAN); + public static final DataType BYTE = new DataType(Type.BYTE); + public static final DataType EMPTY = new DataType(Type.EMPTY); + public static final DataType VEC2 = new DataType(Type.VEC2); + public static final DataType VEC3 = new DataType(Type.VEC3); public static enum Type { - //@JsonEntry - DOUBLE, - FLOAT, - INT, - BYTE, - BOOLEAN, - EMPTY, - VEC2, - VEC3, - STRUCT, - ARRAY - } + // @JsonEntry + DOUBLE(Double.class, double[].class, "double", 8), + FLOAT(Float.class, float[].class, "float", 4), + INT(Integer.class, int[].class, "int", 4), + BYTE(Byte.class, byte[].class, "byte", 1), + BOOLEAN(Boolean.class, boolean[].class, "boolean", 1), + EMPTY(null, null, "void", 0), + VEC2(Vec2.class, Vec2[].class, "Vec2", 16), + VEC3(Vec3.class, Vec3[].class, "Vec3", 24), + STRUCT(null, null, "struct", -1), + ARRAY(null, null, "array", -1); - @Override - public String toString(){ - switch(type){ - case DOUBLE: return "double"; - case FLOAT: return "float"; - case INT: return "int"; - case BYTE: return "byte"; - case BOOLEAN: return "bool"; - case EMPTY: return "void"; - case VEC2: return "Vec2"; - case VEC3: return "Vec3"; - case STRUCT: return "struct"; - case ARRAY: return "array"; - default: return "UNKNOWN_TYPE"; + Type(Class c, Class arrayC, String name, int size) { + this.c = c; + this.arrayC = arrayC; + this.name = name; + this.size = size; } + + public final Class c; + public final Class arrayC; + public final String name; + public final int size; } - public int getDataSize(){ - switch (type){ - case DOUBLE: return 8; - case FLOAT: return 4; - case INT: return 4; - case BYTE: return 1; - case BOOLEAN: return 1; - case EMPTY: return 0; - case VEC2: return 16; - case VEC3: return 24; - default: - return -1; //Should not occur - } + @Override + public String toString() { + return type.name; } - + public int getDataSize() { + return type.size; + } public Type type; /// The virtual message size in bytes - //public int dataSize; + // public int dataSize; - public DataType(Type type){ + public DataType(Type type) { this.type = type; - //this.dataSize = getDataSize(); + // this.dataSize = getDataSize(); } // protected DataType(Type type, int dataSize){ - // this.type = type; - // this.dataSize = dataSize; + // this.type = type; + // this.dataSize = dataSize; // } - - @Override public int hashCode() { @@ -89,47 +79,25 @@ public class DataType { } @Override - public boolean equals(Object o){ + public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (this.getClass() != o.getClass()) return false; - return this.type == ((DataType)o).type; + return this.type == ((DataType) o).type; } - public void toJson(JsonWriter j, Object o) { - switch(type){ - case DOUBLE: j.writeValue((Double)o); return; - case FLOAT: j.writeValue((Float)o); return; - case INT: j.writeValue((Integer)o); return; - case BYTE: j.writeValue((Byte)o); return; - case BOOLEAN: j.writeValue((Boolean)o); return; - case EMPTY: return; - case VEC2: ((Vec2)o).toJson(j); return; - case VEC3: ((Vec3)o).toJson(j); return; - default: throw new IllegalArgumentException("Missing type implementation (type: "+type+")"); - } + public void toJson(JsonWriter j, Object o, SerializationContext context) throws IllegalArgumentException, IllegalAccessException { + if (o == null) + return; + Json.toJson(j, o, context); } - public Object fromJson(JsonTraverser j){ - switch(type){ - case DOUBLE: return j.getDouble(); - case FLOAT: return (float)j.getDouble(); - case INT: return (int)j.getLong(); - case BYTE: return (byte)j.getLong(); - case BOOLEAN: return j.getBoolean(); - case EMPTY: return null; - case VEC2: - Vec2 v = new Vec2(); - v.fromJson(j); - return v; - case VEC3: - Vec3 v2 = new Vec3(); - v2.fromJson(j); - return v2; - default: throw new IllegalArgumentException("Missing type implementation (type: "+type+")"); - } + public Object fromJson(JsonTraverser j, SerializationContext context) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + if (type.c == null) return null; + return Json.instantiateFromJson(j, type.c, context); } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/DynamicObject.java b/src/main/java/de/rwth/montisim/commons/simulation/DynamicObject.java index 35aec99..466d6fd 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/DynamicObject.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/DynamicObject.java @@ -1,17 +1,15 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.simulation; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonWriter; -import de.rwth.montisim.commons.utils.StringRef; import de.rwth.montisim.commons.utils.Vec3; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; +import de.rwth.montisim.commons.utils.json.Typed; /** * Represents a dynamic object in the simulation, does have positional and rotation velocities and a mass. */ +@Typed("dynamic") public class DynamicObject extends StaticObject { - public static final String TYPE = "dynamic"; + //public static final String TYPE = "dynamic"; public Vec3 velocity; public Vec3 angularVelocity; public double mass; @@ -27,45 +25,4 @@ public class DynamicObject extends StaticObject { return "{name: " + name + ", type: " + desc + ", mass: " + mass + ", pos: " + pos + ", vel: " + velocity + "}"; } - - - - public static final String K_VEL = "vel"; - public static final String K_ANGULAR_VEL = "ang_vel"; - public static final String K_MASS = "mass"; - - @Override - public void toJson(JsonWriter j) { - j.startObject(); - j.write(JsonTraverser.K_TYPE, TYPE); - writeStatic(j); - writeDynamic(j); - j.endObject(); - } - - protected void writeDynamic(JsonWriter j){ - j.writeKey(K_VEL); - velocity.toJson(j); - j.writeKey(K_ANGULAR_VEL); - angularVelocity.toJson(j); - j.write(K_MASS, mass); - } - - @Override - public void fromJson(JsonTraverser j) { - for (Entry e : j.expectStructureType(TYPE)){ - if (!readStatic(j, e.key) && !readDynamic(j, e.key)) j.unexpected(e); - } - } - - protected boolean readDynamic(JsonTraverser j, StringRef key){ - if (key.equals(K_VEL)) { - velocity.fromJson(j); - } else if (key.equals(K_ANGULAR_VEL)) { - angularVelocity.fromJson(j); - } else if (key.equals(K_MASS)) { - mass = j.getDouble(); - } else return false; - return true; - } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/ISimulator.java b/src/main/java/de/rwth/montisim/commons/simulation/ISimulator.java index 1cbaaac..cfd674d 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/ISimulator.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/ISimulator.java @@ -6,14 +6,14 @@ package de.rwth.montisim.commons.simulation; */ public interface ISimulator { - void registerStaticObject(StaticObject staticObject); + void registerStaticObject(SimulationObject obj, StaticObject staticObject); /// If registering a dynamic object, do not register it as static - void registerDynamicObject(DynamicObject dynObject); + void registerDynamicObject(SimulationObject obj, DynamicObject dynObject); - void registerUpdatable(Updatable updatable); + void registerUpdatable(SimulationObject obj, Updatable updatable); - void registerDestroyable(Destroyable destroyable); + void registerDestroyable(SimulationObject obj, Destroyable destroyable); - void registerTaskRunner(TaskRunner runner); + void registerTaskRunner(SimulationObject obj, TaskRunner runner); } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/ISimulatorState.java b/src/main/java/de/rwth/montisim/commons/simulation/ISimulatorState.java new file mode 100644 index 0000000..f753c38 --- /dev/null +++ b/src/main/java/de/rwth/montisim/commons/simulation/ISimulatorState.java @@ -0,0 +1,5 @@ +package de.rwth.montisim.commons.simulation; + +public interface ISimulatorState { + +} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/PhysicalValue.java b/src/main/java/de/rwth/montisim/commons/simulation/PhysicalValue.java index af8975c..942e318 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/PhysicalValue.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/PhysicalValue.java @@ -1,36 +1,58 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.simulation; +import java.lang.reflect.InvocationTargetException; + import de.rwth.montisim.commons.dynamicinterface.DataType; +import de.rwth.montisim.commons.utils.json.CustomJson; +import de.rwth.montisim.commons.utils.json.JsonTraverser; +import de.rwth.montisim.commons.utils.json.JsonTraverser.ObjectIterable; +import de.rwth.montisim.commons.utils.json.JsonWriter; +import de.rwth.montisim.commons.utils.json.SerializationContext; /** - * Wrapper around an Object that represents a physical value inside - * the simulation. The goal is that multiple users can read and set the value. - * The getter can be overloaded to hide more complex reading mechanisms for the users. + * Wrapper around an Object that represents a physical value inside the + * simulation. The goal is that multiple users can read and set the value. The + * getter can be overloaded to hide more complex reading mechanisms for the + * users. */ -public class PhysicalValue { +public class PhysicalValue implements CustomJson { protected Object value; - public final String name; - public final DataType type; - public PhysicalValue(String name, DataType type, Object startValue){ + public final transient String name; + public final transient DataType type; + + public PhysicalValue(String name, DataType type, Object startValue) { this.name = name; this.type = type; this.value = startValue; } - // These 2 functions can be overwritten to add some transformation on how to set/get the value. - public Object get(){ + // These 2 functions can be overwritten to add some transformation on how to + // set/get the value. + public Object get() { return value; } - public void set(Object value){ + + public void set(Object value) { this.value = value; } - /** - * This function returns returns the internal value. - * It should not be overwritten. + /** + * This function returns returns the internal value. It should not be + * overwritten. */ - public Object value(){ + public Object value() { return value; } + + @Override + public void write(JsonWriter w, SerializationContext context) throws IllegalAccessException { + type.toJson(w, value, context); + } + + @Override + public void read(JsonTraverser t, ObjectIterable it, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + value = type.fromJson(t, context); + } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/SimulationObject.java b/src/main/java/de/rwth/montisim/commons/simulation/SimulationObject.java index ef25a6e..1e6339e 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/SimulationObject.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/SimulationObject.java @@ -7,10 +7,12 @@ package de.rwth.montisim.commons.simulation; * The simulator must call 'registerComponents()' on the object when it is added to the simulation. * This allows the SimulationObject to register its different components by the Simulator. */ -public interface SimulationObject { +public abstract class SimulationObject { /** * Implementations of this function must register their sub-components by the * simulator: Static/DynamicObject, Updatable, Destroyable */ - public void registerComponents(ISimulator simulator); + public abstract void registerComponents(ISimulator simulator); + + public ISimulatorState state; } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/StaticObject.java b/src/main/java/de/rwth/montisim/commons/simulation/StaticObject.java index a45c10a..782d856 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/StaticObject.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/StaticObject.java @@ -4,23 +4,19 @@ package de.rwth.montisim.commons.simulation; import java.util.Optional; import de.rwth.montisim.commons.boundingbox.BoundingBox; -import de.rwth.montisim.commons.utils.JsonSerializable; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonWriter; import de.rwth.montisim.commons.utils.Mat3; -import de.rwth.montisim.commons.utils.StringRef; import de.rwth.montisim.commons.utils.Vec3; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; -import de.rwth.montisim.commons.utils.JsonTraverser.ObjectIterable; +import de.rwth.montisim.commons.utils.json.Typed; /** * Data for a static object of the simulation world (cannot dynamically move, * and has "infinite" mass) with a bounding box. */ -public class StaticObject implements JsonSerializable { - public static final String TYPE = "static"; - public String name; - public String desc; +@Typed("static") +public class StaticObject { + //public static final String TYPE = "static"; + public transient String name; + public transient String desc; public Vec3 pos; public Mat3 rotation; public Optional bbox; @@ -37,56 +33,4 @@ public class StaticObject implements JsonSerializable { return "{name: " + name + ", desc: " + desc + ", pos: " + pos + "}"; } - - - - - public static final String K_NAME = "name"; - public static final String K_DESC = "desc"; - public static final String K_POS = "pos"; - public static final String K_ROT = "rot"; - public static final String K_BBOX = "bbox"; - - @Override - public void toJson(JsonWriter j) { - j.startObject(); - j.write(JsonTraverser.K_TYPE, TYPE); - writeStatic(j); - j.endObject(); - } - - protected void writeStatic(JsonWriter j){ - j.write(K_NAME, name); - j.write(K_DESC, desc); - j.writeKey(K_POS); - pos.toJson(j); - j.writeKey(K_ROT); - rotation.toJson(j); - if (bbox.isPresent()){ - j.writeKey(K_BBOX); - bbox.get().toJson(j); - } - } - - @Override - public void fromJson(JsonTraverser j) { - for (Entry e : j.expectStructureType(TYPE)){ - if (!readStatic(j, e.key)) j.unexpected(e); - } - } - - protected boolean readStatic(JsonTraverser j, StringRef key){ - if (key.equals(K_NAME)) { - name = j.getString().getJsonString(); - } else if (key.equals(K_DESC)) { - desc = j.getString().getJsonString(); - } else if (key.equals(K_POS)) { - pos.fromJson(j); - } else if (key.equals(K_ROT)) { - rotation.fromJson(j); - } else if (key.equals(K_BBOX)) { - bbox = Optional.of(BoundingBox.buildFromJson(j)); - } else return false; - return true; - } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/simulation/Updater.java b/src/main/java/de/rwth/montisim/commons/simulation/Updater.java index a5f6d8f..e98d9b5 100644 --- a/src/main/java/de/rwth/montisim/commons/simulation/Updater.java +++ b/src/main/java/de/rwth/montisim/commons/simulation/Updater.java @@ -10,7 +10,7 @@ import java.util.List; * applyUpdate() will update all registered objects. */ public class Updater { - protected List updatables = new ArrayList<>(); + protected transient List updatables = new ArrayList<>(); /** Register an Updatable. */ public void addUpdatable(Updatable u){ diff --git a/src/main/java/de/rwth/montisim/commons/utils/JsonSerializable.java b/src/main/java/de/rwth/montisim/commons/utils/JsonSerializable.java deleted file mode 100644 index fef5ff5..0000000 --- a/src/main/java/de/rwth/montisim/commons/utils/JsonSerializable.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.rwth.montisim.commons.utils; - -public interface JsonSerializable { - void toJson(JsonWriter j); - void fromJson(JsonTraverser j); -} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/Mat2.java b/src/main/java/de/rwth/montisim/commons/utils/Mat2.java index e0a569b..86fc673 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Mat2.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Mat2.java @@ -1,9 +1,10 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.*; -public class Mat2 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Mat2 { public Vec2 col1; public Vec2 col2; @@ -134,25 +135,25 @@ public class Mat2 implements JsonSerializable { } } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - col1.toJson(j); - col2.toJson(j); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: col1.fromJson(j); break; - case 2: col2.fromJson(j); break; - } - ++i; - } - if (i < 3) throw new ParsingException("Missing entries in matrix array"); - if (i > 3) throw new ParsingException("Too many entries in matrix array"); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startArray(); + // col1.toJson(j); + // col2.toJson(j); + // j.endArray(); + // } + + // @Override + // public void fromJson(JsonTraverser j) { + // int i = 1; + // for (ValueType t : j.streamArray()){ + // switch(i){ + // case 1: col1.fromJson(j); break; + // case 2: col2.fromJson(j); break; + // } + // ++i; + // } + // if (i < 3) throw new ParsingException("Missing entries in matrix array"); + // if (i > 3) throw new ParsingException("Too many entries in matrix array"); + // } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/Mat3.java b/src/main/java/de/rwth/montisim/commons/utils/Mat3.java index dacc3ac..a2bbc1c 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Mat3.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Mat3.java @@ -1,9 +1,10 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.*; -public class Mat3 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Mat3 { public Vec3 col1; public Vec3 col2; public Vec3 col3; @@ -201,27 +202,27 @@ public class Mat3 implements JsonSerializable { } } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - col1.toJson(j); - col2.toJson(j); - col3.toJson(j); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: col1.fromJson(j); break; - case 2: col2.fromJson(j); break; - case 3: col3.fromJson(j); break; - } - ++i; - } - if (i < 4) throw new ParsingException("Missing entries in matrix array"); - if (i > 4) throw new ParsingException("Too many entries in matrix array"); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startArray(); + // col1.toJson(j); + // col2.toJson(j); + // col3.toJson(j); + // j.endArray(); + // } + + // @Override + // public void fromJson(JsonTraverser j) { + // int i = 1; + // for (ValueType t : j.streamArray()){ + // switch(i){ + // case 1: col1.fromJson(j); break; + // case 2: col2.fromJson(j); break; + // case 3: col3.fromJson(j); break; + // } + // ++i; + // } + // if (i < 4) throw new ParsingException("Missing entries in matrix array"); + // if (i > 4) throw new ParsingException("Too many entries in matrix array"); + // } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/Mat4.java b/src/main/java/de/rwth/montisim/commons/utils/Mat4.java index 606e85b..d382479 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Mat4.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Mat4.java @@ -1,10 +1,11 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.*; // TODO add constructors as in Mat3 -public class Mat4 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Mat4 { public Vec4 col1; public Vec4 col2; public Vec4 col3; @@ -71,29 +72,4 @@ public class Mat4 implements JsonSerializable { } } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - col1.toJson(j); - col2.toJson(j); - col3.toJson(j); - col4.toJson(j); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: col1.fromJson(j); break; - case 2: col2.fromJson(j); break; - case 3: col3.fromJson(j); break; - case 4: col4.fromJson(j); break; - } - ++i; - } - if (i < 5) throw new ParsingException("Missing entries in matrix array"); - if (i > 5) throw new ParsingException("Too many entries in matrix array"); - } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/Time.java b/src/main/java/de/rwth/montisim/commons/utils/Time.java index 0979e09..9377f67 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Time.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Time.java @@ -4,8 +4,6 @@ package de.rwth.montisim.commons.utils; import java.time.Duration; import java.time.Instant; -import de.rwth.montisim.commons.utils.JsonTraverser.ArrayEntryStream; - public class Time { public static final long SECOND_TO_NANOSEC = 1000000000; public static final double NANOSEC_TO_SEC = 0.000000001; @@ -19,23 +17,4 @@ public class Time { public static Duration durationFromSeconds(double seconds){ return Duration.ofNanos((long)(seconds*SECOND_TO_NANOSEC)); } - - public static void toJson(JsonWriter j, Instant t) { - j.startArray(); - j.writeValue(t.getEpochSecond()); - j.writeValue(t.getNano()); - j.endArray(); - } - - public static Instant fromJson(JsonTraverser j){ - ArrayEntryStream it = j.streamArray().it; - if (!it.hasNext()) throw new ParsingException("Expected 'Seconds' entry"); - it.next(); - long sec = j.getLong(); - if (!it.hasNext()) throw new ParsingException("Expected 'nanoseconds' entry"); - it.next(); - long nano = j.getLong(); - Instant t = Instant.ofEpochSecond(sec, nano); - return t; - } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/Vec2.java b/src/main/java/de/rwth/montisim/commons/utils/Vec2.java index fa821a3..0beec47 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Vec2.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Vec2.java @@ -1,14 +1,14 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; import de.rwth.montisim.commons.utils.json.*; /** * Implementation of the javafx Point2D class. * https://docs.oracle.com/javase/8/javafx/api/javafx/geometry/Point2D.html */ -public class Vec2 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Vec2 { @JsonEntry public double x; @JsonEntry @@ -177,25 +177,25 @@ public class Vec2 implements JsonSerializable { } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - j.writeValue(x); - j.writeValue(y); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: x = j.getDouble(); break; - case 2: y = j.getDouble(); break; - } - ++i; - } - if (i < 3) throw new ParsingException("Missing entries in vector array"); - if (i > 3) throw new ParsingException("Too many entries in vector array"); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startArray(); + // j.writeValue(x); + // j.writeValue(y); + // j.endArray(); + // } + + // @Override + // public void fromJson(JsonTraverser j) { + // int i = 1; + // for (ValueType t : j.streamArray()){ + // switch(i){ + // case 1: x = j.getDouble(); break; + // case 2: y = j.getDouble(); break; + // } + // ++i; + // } + // if (i < 3) throw new ParsingException("Missing entries in vector array"); + // if (i > 3) throw new ParsingException("Too many entries in vector array"); + // } } diff --git a/src/main/java/de/rwth/montisim/commons/utils/Vec3.java b/src/main/java/de/rwth/montisim/commons/utils/Vec3.java index 2477752..fae583f 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Vec3.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Vec3.java @@ -1,13 +1,14 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.*; /** * Implementation of the javafx Point3D class. * https://docs.oracle.com/javase/8/javafx/api/javafx/geometry/Point3D.html */ -public class Vec3 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Vec3 { public double x; public double y; public double z; @@ -196,27 +197,27 @@ public class Vec3 implements JsonSerializable { } } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - j.writeValue(x); - j.writeValue(y); - j.writeValue(z); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: x = j.getDouble(); break; - case 2: y = j.getDouble(); break; - case 3: z = j.getDouble(); break; - } - ++i; - } - if (i < 4) throw new ParsingException("Missing entries in vector array"); - if (i > 4) throw new ParsingException("Too many entries in vector array"); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startArray(); + // j.writeValue(x); + // j.writeValue(y); + // j.writeValue(z); + // j.endArray(); + // } + + // @Override + // public void fromJson(JsonTraverser j) { + // int i = 1; + // for (ValueType t : j.streamArray()){ + // switch(i){ + // case 1: x = j.getDouble(); break; + // case 2: y = j.getDouble(); break; + // case 3: z = j.getDouble(); break; + // } + // ++i; + // } + // if (i < 4) throw new ParsingException("Missing entries in vector array"); + // if (i > 4) throw new ParsingException("Too many entries in vector array"); + // } } diff --git a/src/main/java/de/rwth/montisim/commons/utils/Vec4.java b/src/main/java/de/rwth/montisim/commons/utils/Vec4.java index aaa5d73..1b9bf14 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/Vec4.java +++ b/src/main/java/de/rwth/montisim/commons/utils/Vec4.java @@ -1,9 +1,10 @@ /* (c) https://github.com/MontiCore/monticore */ package de.rwth.montisim.commons.utils; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.*; -public class Vec4 implements JsonSerializable { +@JsonType(Type.ARRAY) +public class Vec4 { public double x; public double y; public double z; @@ -168,29 +169,29 @@ public class Vec4 implements JsonSerializable { } - @Override - public void toJson(JsonWriter j) { - j.startArray(); - j.writeValue(x); - j.writeValue(y); - j.writeValue(z); - j.writeValue(w); - j.endArray(); - } - - @Override - public void fromJson(JsonTraverser j) { - int i = 1; - for (ValueType t : j.streamArray()){ - switch(i){ - case 1: x = j.getDouble(); break; - case 2: y = j.getDouble(); break; - case 3: z = j.getDouble(); break; - case 4: z = j.getDouble(); break; - } - ++i; - } - if (i < 5) throw new ParsingException("Missing entries in vector array"); - if (i > 5) throw new ParsingException("Too many entries in vector array"); - } + // @Override + // public void toJson(JsonWriter j) { + // j.startArray(); + // j.writeValue(x); + // j.writeValue(y); + // j.writeValue(z); + // j.writeValue(w); + // j.endArray(); + // } + + // @Override + // public void fromJson(JsonTraverser j) { + // int i = 1; + // for (ValueType t : j.streamArray()){ + // switch(i){ + // case 1: x = j.getDouble(); break; + // case 2: y = j.getDouble(); break; + // case 3: z = j.getDouble(); break; + // case 4: z = j.getDouble(); break; + // } + // ++i; + // } + // if (i < 5) throw new ParsingException("Missing entries in vector array"); + // if (i > 5) throw new ParsingException("Too many entries in vector array"); + // } } diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/CustomJson.java b/src/main/java/de/rwth/montisim/commons/utils/json/CustomJson.java new file mode 100644 index 0000000..30ee324 --- /dev/null +++ b/src/main/java/de/rwth/montisim/commons/utils/json/CustomJson.java @@ -0,0 +1,10 @@ +package de.rwth.montisim.commons.utils.json; + +import java.lang.reflect.InvocationTargetException; + +import de.rwth.montisim.commons.utils.json.JsonTraverser.ObjectIterable; + +public interface CustomJson { + void write(JsonWriter w, SerializationContext context) throws IllegalAccessException; + void read(JsonTraverser t, ObjectIterable it, SerializationContext context) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; +} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/FieldOptional.java b/src/main/java/de/rwth/montisim/commons/utils/json/FieldOptional.java index 248e61c..048f99b 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/json/FieldOptional.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/FieldOptional.java @@ -1,6 +1,9 @@ package de.rwth.montisim.commons.utils.json; -public enum FieldOptional { - EXPLICIT, - ALL +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface FieldOptional { + Select value() default Select.ALL; } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/FieldSelect.java b/src/main/java/de/rwth/montisim/commons/utils/json/FieldSelect.java index 702bc44..f3dc83f 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/json/FieldSelect.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/FieldSelect.java @@ -1,6 +1,9 @@ package de.rwth.montisim.commons.utils.json; -public enum FieldSelect { - EXPLICIT, - ALL +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface FieldSelect { + Select value() default Select.ALL; } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/Json.java b/src/main/java/de/rwth/montisim/commons/utils/json/Json.java index bfdc9d0..1ba856d 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/json/Json.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/Json.java @@ -2,368 +2,909 @@ package de.rwth.montisim.commons.utils.json; import java.io.File; import java.io.IOException; -import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; import java.util.Iterator; +import java.util.Optional; +import java.util.Stack; +import java.util.Vector; -import de.rwth.montisim.commons.utils.JsonTraverser; -import de.rwth.montisim.commons.utils.JsonWriter; -import de.rwth.montisim.commons.utils.ParsingException; -import de.rwth.montisim.commons.utils.JsonTraverser.ArrayIterable; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.*; +import de.rwth.montisim.commons.utils.json.JsonTraverser.*; // TODO handle exceptions locally ? -public class Json { +public abstract class Json { public static final String K_TYPE = "type"; - JsonWriter w; - JsonTraverser t; + + // JsonWriter w; + // JsonTraverser t; protected Json() { } - public static Json formattedWriter() { - Json j = new Json(); - j.w = new JsonWriter(); - j.w.init(); - j.w.format = true; - return j; + public static String toFormattedJson(Object o, SerializationContext context) + throws IllegalArgumentException, IllegalAccessException { + JsonWriter w = new JsonWriter(true); + Json.getTypeInfo(o.getClass()).writer.write(w, o, context); + return w.getString(); + } + + public static String toFormattedJson(Object o) throws IllegalArgumentException, IllegalAccessException { + return toFormattedJson(o, null); + } + + public static String toJson(Object o, SerializationContext context) + throws IllegalArgumentException, IllegalAccessException { + JsonWriter w = new JsonWriter(false); + toJson(w, o, context); + return w.getString(); } - public static Json traverser(String json) throws IOException { - Json j = new Json(); - j.t = new JsonTraverser(); - j.t.init(json); - return j; + public static String toJson(Object o) throws IllegalArgumentException, IllegalAccessException { + return toJson(o, null); + } + + public static void toJson(JsonWriter w, Object o, SerializationContext context) + throws IllegalArgumentException, IllegalAccessException { + Json.getTypeInfo(o.getClass()).writer.write(w, o, context); } - public static Json traverser(File json) throws IOException { - Json j = new Json(); - j.t = new JsonTraverser(); - j.t.init(json); - return j; + public static T instantiateFromJson(String json, Class c, SerializationContext context) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + JsonTraverser t = new JsonTraverser(); + t.init(json); + return (T) instantiateFromJson(t, c, context); } - public void toJson(Object o) throws IllegalArgumentException, IllegalAccessException { + public static T instantiateFromJson(String json, Class c) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + return instantiateFromJson(json, c, null); + } + + public static T instantiateFromJson(File json, Class c, SerializationContext context) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + JsonTraverser t = new JsonTraverser(); + t.init(json); + return (T) instantiateFromJson(t, c, context); + } + + public static T instantiateFromJson(File json, Class c) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + return (T) instantiateFromJson(json, c, null); + } + + public static T instantiateFromJson(JsonTraverser t, Class c, SerializationContext context) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException { + TypeInfo inf = getTypeInfo(c); + if (inf.isGeneric) + throw new IllegalArgumentException( + "Trying to instantiate generic class" + c.getSimpleName() + " not from class field."); + if (inf.subtyped) { + ObjectIterable it = t.streamObject(); + if (!it.iterator().hasNext()) + t.expected(K_TYPE); + Entry e = it.iterator().next(); + if (!e.key.equals(K_TYPE)) + t.expected(K_TYPE); + String rType = t.getString().getRawString(); + if (!rType.equals(inf.type)) { + Class subC = inf.subtypes.get(rType); + if (subC == null) + t.unexpected(e); + return (T) registry.get(subC).instancer.instance(t, subC, it, context); + } + } + return (T) inf.instancer.instance(t, c, null, context); + } + + public static void fromJson(String json, Object o) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + fromJson(json, o, null); + } + + public static void fromJson(String json, Object o, SerializationContext context) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + JsonTraverser t = new JsonTraverser(); + t.init(json); + fromJson(t, o, context); + } + + public static void fromJson(File json, Object o) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + fromJson(json, o, null); + } + + public static void fromJson(File json, Object o, SerializationContext context) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException, IOException { + JsonTraverser t = new JsonTraverser(); + t.init(json); + fromJson(t, o, context); + } + + public static void fromJson(JsonTraverser t, Object o, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { Class c = o.getClass(); - if (basicTypeToJson(o, c)) + TypeInfo inf = getTypeInfo(c); + if (inf.reader == null) + throw new IllegalArgumentException("Object type does not support in place json reading."); + inf.reader.read(t, o, null, context); + } + + static final HashMap, PrimitiveWriter> primitiveWriterRegistry; + static final HashMap, PrimitiveReader> primitiveReaderRegistry; + + static void registerPrimitive(Class c, PrimitiveWriter pw, PrimitiveReader pr) { + primitiveWriterRegistry.put(c, pw); + primitiveReaderRegistry.put(c, pr); + } + + static class FieldInfo { + final String name; + final boolean primitive; + final Class c; + final boolean isGeneric; + final java.lang.reflect.Type[] genericC; + final Field f; + + FieldInfo(Field f, JsonEntry je) throws IllegalArgumentException, IllegalAccessException { + this.f = f; + String n = je != null ? je.value() : null; + if (n == null || n.length() == 0) + n = f.getName(); + name = n; + c = f.getType(); + primitive = c.isPrimitive(); + TypeInfo inf = getTypeInfo(c); + isGeneric = inf.isGeneric; + if (inf.isGeneric) { + ParameterizedType pt = (ParameterizedType) f.getGenericType(); + genericC = pt.getActualTypeArguments(); + } else + genericC = null; + + } + } + + static class TypeInfo { + Writer writer; + Instancer instancer; + Reader reader; + boolean subtyped = false; + HashMap> subtypes; + String type; + boolean isGeneric = false; + GenericInstancer genericInstancer; + GenericReader genericReader; + + private TypeInfo() { + } + + public static TypeInfo newTypeInfo(Writer w, Instancer i) { + TypeInfo inf = new TypeInfo(); + inf.writer = w; + inf.instancer = i; + return inf; + } + + public static TypeInfo newGenericTypeInfo(Writer w, GenericInstancer i) { + TypeInfo inf = new TypeInfo(); + inf.writer = w; + inf.isGeneric = true; + inf.genericInstancer = i; + return inf; + } + + public static TypeInfo newGenericTypeInfo(Writer w, GenericReader r) { + TypeInfo inf = new TypeInfo(); + inf.writer = w; + inf.isGeneric = true; + inf.genericReader = r; + return inf; + } + + public static TypeInfo newSubtyped() { + TypeInfo inf = new TypeInfo(); + inf.subtyped = true; + inf.subtypes = new HashMap<>(); + inf.type = ""; + return inf; + } + + public static TypeInfo newTypeInfo(Writer w, Instancer i, Reader r, String type) { + TypeInfo inf = new TypeInfo(); + inf.writer = w; + inf.instancer = i; + inf.reader = r; + inf.type = type; + return inf; + } + } + + static final HashMap, TypeInfo> registry; + + private static TypeInfo getTypeInfo(Class c) throws IllegalArgumentException, IllegalAccessException { + TypeInfo inf = registry.get(c); + if (inf != null) + return inf; + registerType(c); + return registry.get(c); + } + + static void register(Class c, Writer w, Instancer i) { + registry.put(c, TypeInfo.newTypeInfo(w, i)); + } + + static void registerGeneric(Class c, Writer w, GenericInstancer i) { + registry.put(c, TypeInfo.newGenericTypeInfo(w, i)); + } + + static void registerGeneric(Class c, Writer w, GenericReader r) { + registry.put(c, TypeInfo.newGenericTypeInfo(w, r)); + } + + public static void registerType(Class c) throws IllegalArgumentException, IllegalAccessException { + if (registry.containsKey(c)) return; - Jsonable ja = c.getDeclaredAnnotation(Jsonable.class); - if (ja == null) - throw new IllegalArgumentException("Object to be serialized has no 'Jsonable' annotation."); - fieldsToJson(o, c, ja, true); - - } - - - public T instanciateFromJson(Class c) throws InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - T o = (T) instantiateBasicType(c); - if (o != null) - return o; - Jsonable ja = c.getDeclaredAnnotation(Jsonable.class); - if (ja == null) - throw new IllegalArgumentException("Object to be instantiated has no 'Jsonable' annotation."); - - o = c.getDeclaredConstructor().newInstance(); - - Typed ty = c.getDeclaredAnnotation(Typed.class); - boolean typed = ty != null; - boolean obj = ja.type() == StructureType.OBJECT; - boolean all = ja.fields() == FieldSelect.ALL; - FieldOptional fo = ja.optional(); - - if (obj){ - Field[] fields = c.getDeclaredFields(); - int fieldCount = fields.length; - - String[] keys = new String[fieldCount]; - - for (int i = 0; i < fields.length; ++i){ - Field f = fields[i]; - if (!isValid(f)) continue; - JsonEntry je = f.getAnnotation(JsonEntry.class); - if (!isUsed(je, all)) continue; - - String key = je != null ? je.value() : null; - if (key == null || key.length() == 0) key = f.getName(); - keys[i] = key; + if (c.isInterface()) { + registry.put(c, TypeInfo.newSubtyped()); + return; + } + + Typed ta = c.getDeclaredAnnotation(Typed.class); + JsonType jt = c.getDeclaredAnnotation(JsonType.class); + FieldSelect fs = c.getDeclaredAnnotation(FieldSelect.class); + + boolean typed = ta != null; + boolean obj = jt == null || jt.value() == Type.OBJECT; + boolean isEnum = c.isEnum(); + boolean all = fs == null || fs.value() == Select.ALL; + boolean abstr = Modifier.isAbstract(c.getModifiers()); + boolean custom = false; + for (Class i : c.getInterfaces()) + if (i.equals(CustomJson.class)) { + custom = true; + break; } + // FieldOptional fo = ja.optional(); - for (Entry e : t.streamObject()){ - int kid = 0; - for (; kid < keys.length; ++kid){ - if (keys[kid] != null && e.key.equals(keys[kid])) break; + if (typed && !obj) + throw new IllegalArgumentException("Classes serialized as arrays cannot be typed."); + + if (typed && isEnum) + throw new IllegalArgumentException("Enums cannot be typed."); + + String type = typed ? ta.value().length() > 0 ? ta.value() : c.getSimpleName() : null; + + Writer writer = null; + Reader reader = null; + Instancer instancer = null; + + if (isEnum) { + HashMap names = new HashMap<>(); + HashMap variantMap = new HashMap<>(); + for (Field f : c.getDeclaredFields()) { + if (f.isEnumConstant()) { + JsonEntry je = f.getDeclaredAnnotation(JsonEntry.class); + String name = f.getName(); + if (je != null && je.value().length() > 0) + name = je.value(); + Object val = f.get(null); + names.put(val, name); + variantMap.put(name, val); } - if (kid == keys.length) t.unexpected(e); - Field f = fields[kid]; - Class fc = f.getType(); - if (fc.isPrimitive()) readPrimitiveField(fc, f, o); - else f.set(o, instanciateFromJson(fc)); + } + writer = (w, o, context) -> { + String name = names.get(o); + w.writeValue(name); + }; + instancer = (t, cl, it, context) -> { + String var = t.getString().getJsonString(); + Object res = variantMap.get(var); + if (res == null) + throw new ParsingException("Invalid variant for enum " + cl.getSimpleName() + ": " + var); + return res; + }; + } else if (custom) { + writer = (w, o, context) -> { + ((CustomJson) o).write(w, context); + }; + if (!abstr) { + instancer = (t, cl, it, context) -> { + Object ob = cl.getDeclaredConstructor().newInstance(); + ((CustomJson) ob).read(t, it, context); + return ob; + }; + reader = (t, o, it, context) -> { + ((CustomJson) o).read(t, it, context); + }; } } else { - Field[] fields = c.getDeclaredFields(); - Field[] entries = new Field[fields.length]; - int eCount = 0; - for (int i = 0; i < fields.length; ++i){ - Field f = fields[i]; - if (!isValid(f)) continue; - JsonEntry je = f.getAnnotation(JsonEntry.class); - if (!isUsed(je, all)) continue; - entries[eCount++] = f; + + final Vector fields = new Vector<>(); + final HashMap map = new HashMap<>(); + getFields(c, fields, map, obj, all); + + if (typed) { + registerSubtype(c, type); } - int i = 0; - for (ValueType v : t.streamArray()){ - if (i >= eCount) throw new ParsingException("Too many entries in "+c.getSimpleName()+" array"); - Field f = entries[i]; - Class fc = f.getType(); - if (fc.isPrimitive()) readPrimitiveField(fc, f, o); - else f.set(o, instanciateFromJson(fc)); - ++i; + + writer = (w, o, context) -> { + if (obj) + w.startObject(); + else + w.startArray(); + + if (typed) { + if (obj) + w.writeKey(K_TYPE); + w.writeValue(type); + } + + for (FieldInfo f : fields) { + if (f.primitive) { + if (obj) + w.writeKey(f.name); + PrimitiveWriter pw = primitiveWriterRegistry.get(f.c); + if (pw == null) + throw new IllegalArgumentException("Missing primitive field type support for: " + f.name); + pw.write(w, o, f.f); + } else { + Object o2 = f.f.get(o); + if (o2 != null) { + if (obj) + w.writeKey(f.name); + Json.getTypeInfo(o2.getClass()).writer.write(w, o2, context); + } + } + } + + if (obj) + w.endObject(); + else + w.endArray(); + }; + + if (!abstr) { + instancer = (t, cl, it, context) -> { + Object ob = cl.getDeclaredConstructor().newInstance(); + if (obj) { + if (it == null) { + it = t.streamObject(); + if (typed) { // Only check type if it was not already + if (!it.iterator().hasNext()) + t.expected(K_TYPE); + Entry e = it.iterator().next(); + if (!e.key.getJsonString().equals(K_TYPE)) + t.expected(K_TYPE); + StringRef rType = t.getString(); + if (!rType.equals(type)) + t.expectedStructureType(type, rType.getRawString()); + } + } + + for (Entry e : it) { + FieldInfo f = map.get(e.key.getJsonString()); + if (f == null) + t.unexpected(e); + + instanceField(t, ob, f, context); + } + } else { + ArrayIterable ita = t.streamArray(); + for (FieldInfo f : fields) { + if (!ita.iterator().hasNext()) + throw new ParsingException("Missing entries in " + c.getSimpleName() + " array"); + ita.iterator().next(); + + instanceField(t, ob, f, context); + } + + if (ita.iterator().hasNext()) + throw new ParsingException("Too many entries in " + c.getSimpleName() + " array"); + } + return ob; + }; + + reader = (t, o, it, context) -> { + if (obj) { + if (it == null) { + it = t.streamObject(); + if (typed) { // Only check type if it was not already + if (!it.iterator().hasNext()) + t.expected(K_TYPE); + Entry e = it.iterator().next(); + if (!e.key.getJsonString().equals(K_TYPE)) + t.expected(K_TYPE); + StringRef rType = t.getString(); + if (!rType.equals(type)) + t.expectedStructureType(type, rType.getRawString()); + } + } + + for (Entry e : it) { + FieldInfo f = map.get(e.key.getJsonString()); + if (f == null) + t.unexpected(e); + readField(t, o, f, context); + } + } else { + ArrayIterable ita = t.streamArray(); + for (FieldInfo f : fields) { + if (!ita.iterator().hasNext()) + throw new ParsingException("Missing entries in " + c.getSimpleName() + " array"); + ita.iterator().next(); + readField(t, o, f, context); + } + + if (ita.iterator().hasNext()) + throw new ParsingException("Too many entries in " + c.getSimpleName() + " array"); + } + }; } - if (i < eCount) throw new ParsingException("Missing entries in "+c.getSimpleName()+" array"); } - return o; - } - private boolean isValid(Field f){ - int mod = f.getModifiers(); - if (Modifier.isStatic(mod) || Modifier.isTransient(mod)) return false; - if (Modifier.isPrivate(mod)) f.setAccessible(true); - return true; + registry.put(c, TypeInfo.newTypeInfo(writer, instancer, reader, type)); } - private boolean isUsed(JsonEntry je, boolean all){ - return je != null || all; + static void instanceField(JsonTraverser t, Object ob, FieldInfo f, SerializationContext c) + throws IllegalAccessException, IllegalArgumentException, InstantiationException, InvocationTargetException, + NoSuchMethodException, SecurityException { + if (f.primitive) { + PrimitiveReader pr = primitiveReaderRegistry.get(f.c); + if (pr == null) + throw new IllegalArgumentException("Missing primitive field type support for: " + f.name); + pr.read(t, ob, f.f); + return; + } + if (f.isGeneric) { + f.f.set(ob, getTypeInfo(f.c).genericInstancer.instance(t, f.genericC, c)); + return; + } + f.f.set(ob, instantiateFromJson(t, f.c, c)); } - private void fieldsToJson(Object o, Class c, Jsonable ja, boolean first) - throws IllegalArgumentException, IllegalAccessException { - Typed t = c.getDeclaredAnnotation(Typed.class); - boolean typed = t != null; - boolean obj = ja.type() == StructureType.OBJECT; - boolean all = ja.fields() == FieldSelect.ALL; - FieldOptional fo = ja.optional(); - - if (first){ - if (obj) w.startObject(); - else w.startArray(); - - if (typed){ - String type = t.value(); - if (type.length() == 0) type = c.getSimpleName(); - if (obj) w.writeKey(K_TYPE); - w.writeValue(type); - } + static void readField(JsonTraverser t, Object o, FieldInfo f, SerializationContext c) + throws IllegalAccessException, IllegalArgumentException, InstantiationException, InvocationTargetException, + NoSuchMethodException, SecurityException { + if (f.primitive) { + PrimitiveReader pr = primitiveReaderRegistry.get(f.c); + if (pr == null) + throw new IllegalArgumentException("Missing primitive field type support for: " + f.name); + pr.read(t, o, f.f); + return; + } + TypeInfo inf = getTypeInfo(f.c); + if (f.isGeneric) { + if (inf.genericReader == null) + f.f.set(o, inf.genericInstancer.instance(t, f.genericC, c)); + else + inf.genericReader.read(t, f.f.get(o), null, f.genericC, c); + return; } - + if (inf.subtyped) { + TypeInfo subInf = getTypeInfo(f.f.get(o).getClass()); + ObjectIterable it = t.streamObject(); + if (!it.iterator().hasNext()) + t.expected(K_TYPE); + Entry e = it.iterator().next(); + if (!e.key.equals(K_TYPE)) + t.expected(K_TYPE); + String rType = t.getString().getRawString(); + if (!rType.equals(subInf.type)) { + Class subC = inf.subtypes.get(rType); + if (subC == null) + t.unexpected(e); + f.f.set(o, registry.get(subC).instancer.instance(t, subC, it, c)); + } else { + if (subInf.reader == null) + f.f.set(o, subInf.instancer.instance(t, f.c, it, c)); + else + subInf.reader.read(t, f.f.get(o), it, c); + } + } else if (inf.reader == null) + f.f.set(o, instantiateFromJson(t, f.c, c)); + else + inf.reader.read(t, f.f.get(o), null, c); + } + + static void getFields(Class c, Vector fields, HashMap map, boolean obj, + boolean all) throws IllegalArgumentException, IllegalAccessException { Class superC = c.getSuperclass(); - Jsonable superJa = superC.getDeclaredAnnotation(Jsonable.class); - if (superJa != null) fieldsToJson(o, superC, superJa, false); - + if (superC != null && !superC.equals(Object.class)) { + JsonType jt = superC.getDeclaredAnnotation(JsonType.class); + boolean superObj = jt == null || jt.value() == Type.OBJECT; + if (superObj != obj) + throw new IllegalArgumentException("Json Subclass has different structure type than superclass. " + + "Subclass " + c.getSimpleName() + "has type: " + (obj ? "object" : "array") + ", super type " + + superC.getSimpleName() + " has type: " + (obj ? "object" : "array")); + FieldSelect fs = superC.getDeclaredAnnotation(FieldSelect.class); + boolean superAll = fs == null || fs.value() == Select.ALL; + + getFields(superC, fields, map, obj, superAll); + } - for(Field f : c.getDeclaredFields()){ - if (!isValid(f)) continue; + for (Field f : c.getDeclaredFields()) { + if (f.isSynthetic()) + continue; + int mod = f.getModifiers(); + if (Modifier.isStatic(mod) || Modifier.isTransient(mod)) + continue; + f.setAccessible(true); JsonEntry je = f.getAnnotation(JsonEntry.class); - if (!isUsed(je, all)) continue; + if (je == null && !all) + continue; - if (obj){ - String name = je != null ? je.value() : null; - if (name == null || name.length() == 0) name = f.getName(); - w.writeKey(name); - } - - Class fc = f.getType(); - if (fc.isPrimitive()) writePrimitiveField(fc, f, o); - else toJson(f.get(o)); - } - if(first){ - if (obj) w.endObject(); - else w.endArray(); + FieldInfo inf = new FieldInfo(f, je); + fields.add(inf); + map.put(inf.name, inf); } } - private void writePrimitiveField(Class fc,Field f, Object o) + static void registerSubtype(Class baseClass, String baseType) throws IllegalArgumentException, IllegalAccessException { - if (fc.equals(double.class)) { - w.writeValue(f.getDouble(o)); - } else if (fc.equals(int.class)) { - w.writeValue(f.getInt(o)); - } else if (fc.equals(float.class)) { - w.writeValue(f.getFloat(o)); - } else if (fc.equals(char.class)) { - w.writeValue(f.getChar(o)); - } else if (fc.equals(byte.class)) { - w.writeValue(f.getByte(o)); - } else if (fc.equals(long.class)) { - w.writeValue(f.getLong(o)); - } else if (fc.equals(short.class)) { - w.writeValue(f.getShort(o)); - } else if (fc.equals(boolean.class)) { - w.writeValue(f.getBoolean(o)); - } else throw new IllegalArgumentException("Unsupported primitive type: "+fc.toString()); - } - - private void readPrimitiveField(Class fc,Field f, Object o) + registerSubtype(baseClass, baseType, baseClass); + } + + static void registerSubtype(Class baseClass, String baseType, Class c) throws IllegalArgumentException, IllegalAccessException { - if (fc.equals(double.class)) { - f.setDouble(o, t.getDouble()); - } else if (fc.equals(int.class)) { - f.setInt(o, (int)t.getLong()); - } else if (fc.equals(float.class)) { - f.setFloat(o, (float)t.getDouble()); - } else if (fc.equals(char.class)) { - f.setChar(o, (char)t.getLong()); - } else if (fc.equals(byte.class)) { - f.setByte(o, (byte)t.getLong()); - } else if (fc.equals(long.class)) { - f.setLong(o, t.getLong()); - } else if (fc.equals(short.class)) { - f.setShort(o, (short)t.getLong()); - } else if (fc.equals(boolean.class)) { - f.setBoolean(o, t.getBoolean()); - } else throw new IllegalArgumentException("Unsupported primitive type: "+fc.toString()); - } - - private boolean basicTypeToJson(Object o, Class c) throws IllegalArgumentException, IllegalAccessException { - if (c.equals(String.class)){ - w.writeValue((String)o); - } else if (Number.class.isAssignableFrom(c)){ - if (c.equals(Double.class)){ - w.writeValue((Double)o); - } else if (c.equals(Integer.class)){ - w.writeValue((Integer)o); - } else if (c.equals(Float.class)){ - w.writeValue((Float)o); - } else if (c.equals(Long.class)){ - w.writeValue((Long)o); - } else if (c.equals(Short.class)){ - w.writeValue((Short)o); - } else if (c.equals(Byte.class)){ - w.writeValue((Byte)o); - } else throw new IllegalArgumentException("Unsupported Number type: "+c.toString()); - } - else if (c.equals(Character.class)){ - w.writeValue((Character)o); - } else if (c.equals(Boolean.class)){ - w.writeValue((Boolean)o); - } else if (c.isArray()) { - w.startArray(); - if (c.equals(double[].class)){ - double[] arr = (double[])o; - w.writeValue(arr.length); - for (double v : arr) w.writeValue(v); - } else if (c.equals(float[].class)){ - float[] arr = (float[])o; - w.writeValue(arr.length); - for (float v : arr) w.writeValue(v); - } else if (c.equals(int[].class)){ - int[] arr = (int[])o; - w.writeValue(arr.length); - for (int v : arr) w.writeValue(v); - } else if (c.equals(long[].class)){ - long[] arr = (long[])o; - w.writeValue(arr.length); - for (long v : arr) w.writeValue(v); - } else if (c.equals(short[].class)){ - short[] arr = (short[])o; - w.writeValue(arr.length); - for (short v : arr) w.writeValue(v); - } else if (c.equals(byte[].class)){ - byte[] arr = (byte[])o; - w.writeValue(arr.length); - for (byte v : arr) w.writeValue(v); - } else if (c.equals(char[].class)){ - char[] arr = (char[])o; - w.writeValue(arr.length); - for (char v : arr) w.writeValue(v); - } else if (c.equals(boolean[].class)){ - boolean[] arr = (boolean[])o; - w.writeValue(arr.length); - for (boolean v : arr) w.writeValue(v); - } else { - Object[] arr = (Object[])o; - w.writeValue(arr.length); - for (Object v : arr) toJson(v); + + Class[] interfaces = c.getInterfaces(); + for (Class i : interfaces) { + TypeInfo iinf = getTypeInfo(i); + if (iinf.subtyped == false) { + iinf.subtyped = true; + iinf.subtypes = new HashMap<>(); } - w.endArray(); - } else return false; - return true; - } - - - private Object instantiateBasicType(Class c) throws InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - if (c.equals(String.class)){ - return t.getString().getJsonString(); - } else if (Number.class.isAssignableFrom(c)){ - if (c.equals(Double.class)){ - return t.getDouble(); - } else if (c.equals(Integer.class)){ - return (int) t.getLong(); - } else if (c.equals(Float.class)){ - return (float) t.getDouble(); - } else if (c.equals(Long.class)){ - return t.getLong(); - } else if (c.equals(Short.class)){ - return (short) t.getLong(); - } else if (c.equals(Byte.class)){ - return (byte) t.getLong(); - } else throw new IllegalArgumentException("Unsupported Number type: "+c.toString()); + iinf.subtypes.put(baseType, baseClass); + } + + Class superC = c.getSuperclass(); + if (superC == null || superC.equals(Object.class)) + return; + + boolean abstr = Modifier.isAbstract(superC.getModifiers()); + if (!abstr && superC.getAnnotation(Typed.class) == null) + throw new IllegalArgumentException("Super-class " + superC.getSimpleName() + " of subtyped class " + + baseClass.getSimpleName() + " is not @Typed"); + TypeInfo inf = getTypeInfo(superC); + if (inf.subtyped == false) { + inf.subtyped = true; + inf.subtypes = new HashMap<>(); } - else if (c.equals(Character.class)){ - return (char) t.getLong(); - } else if (c.equals(Boolean.class)){ - return t.getBoolean(); - } else if (c.isArray()) { - ArrayIterable it = t.streamArray(); - if (!it.iterator().hasNext()) throw new ParsingException("Expected array length at start of array"); + inf.subtypes.put(baseType, baseClass); + + registerSubtype(baseClass, baseType, superC); + } + + static public interface Writer { + void write(JsonWriter w, Object o, SerializationContext context) throws IllegalAccessException; + } + + static public interface Instancer { + Object instance(JsonTraverser t, Class c, ObjectIterable it, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; + } + + static public interface Reader { + void read(JsonTraverser t, Object o, ObjectIterable it, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; + } + + static interface PrimitiveWriter { + void write(JsonWriter w, Object o, Field f) throws IllegalAccessException; + } + + static interface PrimitiveReader { + void read(JsonTraverser t, Object o, Field f) throws IllegalAccessException; + } + + static interface GenericInstancer { + Object instance(JsonTraverser t, java.lang.reflect.Type[] generics, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; + } + + static interface GenericReader { + void read(JsonTraverser t, Object o, ObjectIterable it, java.lang.reflect.Type[] generics, + SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; + } + + static { + registry = new HashMap<>(); + primitiveWriterRegistry = new HashMap<>(); + primitiveReaderRegistry = new HashMap<>(); + + registerPrimitive(double.class, (w, o, f) -> w.writeValue(f.getDouble(o)), + (t, o, f) -> f.setDouble(o, t.getDouble())); + registerPrimitive(int.class, (w, o, f) -> w.writeValue(f.getInt(o)), + (t, o, f) -> f.setInt(o, (int) t.getLong())); + registerPrimitive(float.class, (w, o, f) -> w.writeValue(f.getFloat(o)), + (t, o, f) -> f.setFloat(o, (float) t.getDouble())); + registerPrimitive(char.class, (w, o, f) -> w.writeValue(f.getChar(o)), + (t, o, f) -> f.setChar(o, (char) t.getLong())); + registerPrimitive(byte.class, (w, o, f) -> w.writeValue(f.getByte(o)), + (t, o, f) -> f.setByte(o, (byte) t.getLong())); + registerPrimitive(long.class, (w, o, f) -> w.writeValue(f.getLong(o)), (t, o, f) -> f.setLong(o, t.getLong())); + registerPrimitive(short.class, (w, o, f) -> w.writeValue(f.getShort(o)), + (t, o, f) -> f.setShort(o, (short) t.getLong())); + registerPrimitive(boolean.class, (w, o, f) -> w.writeValue(f.getBoolean(o)), + (t, o, f) -> f.setBoolean(o, t.getBoolean())); + registerPrimitive(Optional.class, (w, o, f) -> w.writeValue(f.getBoolean(o)), + (t, o, f) -> f.setBoolean(o, t.getBoolean())); + + register(String.class, (j, o, c) -> j.writeValue((String) o), (j, o, i, c) -> j.getString().getJsonString()); + register(Double.class, (j, o, c) -> j.writeValue((Double) o), (j, o, i, c) -> j.getDouble()); + register(Float.class, (j, o, c) -> j.writeValue((Float) o), (j, o, i, c) -> (float) j.getDouble()); + register(Long.class, (j, o, c) -> j.writeValue((Long) o), (j, o, i, c) -> j.getLong()); + register(Integer.class, (j, o, c) -> j.writeValue((Integer) o), (j, o, i, c) -> (int) j.getLong()); + register(Short.class, (j, o, c) -> j.writeValue((Short) o), (j, o, i, c) -> (short) j.getLong()); + register(Byte.class, (j, o, c) -> j.writeValue((Byte) o), (j, o, i, c) -> (byte) j.getLong()); + register(Character.class, (j, o, c) -> j.writeValue((Character) o), (j, o, i, c) -> (char) j.getLong()); + register(Boolean.class, (j, o, c) -> j.writeValue((Boolean) o), (j, o, i, c) -> j.getBoolean()); + register(double[].class, (j, o, c) -> { + j.startArray(); + double[] arr = (double[]) o; + j.writeValue(arr.length); + for (double v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + double[] arr = new double[length]; + for (ValueType ty : it) + arr[i++] = j.getDouble(); + return arr; + }); + register(float[].class, (j, o, c) -> { + j.startArray(); + float[] arr = (float[]) o; + j.writeValue(arr.length); + for (float v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + float[] arr = new float[length]; + for (ValueType ty : it) + arr[i++] = (float) j.getDouble(); + return arr; + }); + register(long[].class, (j, o, c) -> { + j.startArray(); + long[] arr = (long[]) o; + j.writeValue(arr.length); + for (long v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + long[] arr = new long[length]; + for (ValueType ty : it) + arr[i++] = j.getLong(); + return arr; + }); + register(int[].class, (j, o, c) -> { + j.startArray(); + int[] arr = (int[]) o; + j.writeValue(arr.length); + for (int v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + int[] arr = new int[length]; + for (ValueType ty : it) + arr[i++] = (int) j.getLong(); + return arr; + }); + register(short[].class, (j, o, c) -> { + j.startArray(); + short[] arr = (short[]) o; + j.writeValue(arr.length); + for (short v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + short[] arr = new short[length]; + for (ValueType ty : it) + arr[i++] = (short) j.getLong(); + return arr; + }); + register(byte[].class, (j, o, c) -> { + j.startArray(); + byte[] arr = (byte[]) o; + j.writeValue(arr.length); + for (short v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + byte[] arr = new byte[length]; + for (ValueType ty : it) + arr[i++] = (byte) j.getLong(); + return arr; + }); + register(char[].class, (j, o, c) -> { + j.startArray(); + char[] arr = (char[]) o; + j.writeValue(arr.length); + for (char v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); it.iterator().next(); - int length = (int)t.getLong(); - int i = 0; - if (c.equals(double[].class)){ - double[] arr = new double[length]; - for (ValueType ty : it) arr[i++] = t.getDouble(); - return arr; - } else if (c.equals(float[].class)){ - float[] arr = new float[length]; - for (ValueType ty : it) arr[i++] = (float) t.getDouble(); - return arr; - } else if (c.equals(int[].class)){ - int[] arr = new int[length]; - for (ValueType ty : it) arr[i++] = (int) t.getLong(); - return arr; - } else if (c.equals(long[].class)){ - long[] arr = new long[length]; - for (ValueType ty : it) arr[i++] = t.getLong(); - return arr; - } else if (c.equals(short[].class)){ - short[] arr = new short[length]; - for (ValueType ty : it) arr[i++] = (short) t.getLong(); - return arr; - } else if (c.equals(byte[].class)){ - byte[] arr = new byte[length]; - for (ValueType ty : it) arr[i++] = (byte) t.getLong(); - return arr; - } else if (c.equals(char[].class)){ - // TODO as String? - char[] arr = new char[length]; - for (ValueType ty : it) arr[i++] = (char) t.getLong(); - return arr; - } else if (c.equals(boolean[].class)){ - boolean[] arr = new boolean[length]; - for (ValueType ty : it) arr[i++] = t.getBoolean(); - return arr; + int length = (int) j.getLong(), i = 0; + char[] arr = new char[length]; + for (ValueType ty : it) + arr[i++] = (char) j.getLong(); + return arr; + }); + register(boolean[].class, (j, o, c) -> { + j.startArray(); + boolean[] arr = (boolean[]) o; + j.writeValue(arr.length); + for (boolean v : arr) + j.writeValue(v); + j.endArray(); + }, (j, o, a, c) -> { + ArrayIterable it = j.streamArray(); + if (!it.iterator().hasNext()) + throw new ParsingException("Expected array length at start of array"); + it.iterator().next(); + int length = (int) j.getLong(), i = 0; + boolean[] arr = new boolean[length]; + for (ValueType ty : it) + arr[i++] = j.getBoolean(); + return arr; + }); + register(Duration.class, (j, o, c) -> { + j.startArray(); + Duration d = (Duration) o; + j.writeValue(d.getSeconds()); + j.writeValue(d.getNano()); + j.endArray(); + }, (j, o, a, c) -> { + Iterator it = j.streamArray().iterator(); + if (!it.hasNext()) + j.expected("seconds"); + it.next(); + long sec = j.getLong(); + if (!it.hasNext()) + j.expected("nanosecs"); + it.next(); + long nano = j.getLong(); + if (it.hasNext()) + j.expected("nothing"); + return Duration.ofSeconds(sec, nano); + }); + register(Instant.class, (j, o, c) -> { + j.startArray(); + Instant d = (Instant) o; + j.writeValue(d.getEpochSecond()); + j.writeValue(d.getNano()); + j.endArray(); + }, (j, o, a, c) -> { + Iterator it = j.streamArray().iterator(); + if (!it.hasNext()) + j.expected("seconds"); + it.next(); + long sec = j.getLong(); + if (!it.hasNext()) + j.expected("nanosecs"); + it.next(); + long nano = j.getLong(); + if (it.hasNext()) + j.expected("nothing"); + return Instant.ofEpochSecond(sec, nano); + }); + registerGeneric(Vector.class, (j, o, c) -> { + j.startArray(); + Vector vec = (Vector) o; + for (Object v : vec) { + Json.toJson(j, v, c); + } + j.endArray(); + }, (t, gc, c) -> { + Vector v = new Vector<>(); + for (ValueType vt : t.streamArray()) { + v.add(Json.instantiateFromJson(t, (Class) gc[0], c)); + } + return v; + }); + registerGeneric(Optional.class, (j, o, c) -> { + Optional opt = (Optional) o; + if (opt.isPresent()) { + Json.toJson(j, opt.get(), c); } else { - Object[] arr = new Object[length]; - for (ValueType ty : it) arr[i++] = instanciateFromJson(c.getComponentType()); - return arr; + j.startObject(); + j.endObject(); + } + }, (t, gc, c) -> { + if (t.isEmpty()) + return Optional.empty(); + return Optional.of(Json.instantiateFromJson(t, (Class) gc[0], c)); + }); + registerGeneric(HashMap.class, (j, o, c) -> { + HashMap map = (HashMap) o; + Iterator it = map.entrySet().iterator(); + j.startObject(); + while (it.hasNext()) { + HashMap.Entry pair = (HashMap.Entry) it.next(); + j.writeKey(pair.getKey()); + Json.toJson(j, pair.getValue(), c); + // it.remove(); // avoids a ConcurrentModificationException + } + j.endObject(); + }, (t, o, it, generics, context) -> { + HashMap map = (HashMap) o; + if (it == null) + it = t.streamObject(); + for (Entry e : it) { + String key = e.key.getJsonString(); + Object o2 = map.get(key); + if (o2 == null) + map.put(key, Json.instantiateFromJson(t, (Class) generics[1], context)); + else + Json.fromJson(t, o2, context); } - } else return null; + }); + registerGeneric(Stack.class, (j, o, c) -> { + Stack stack = (Stack) o; + j.startArray(); + Iterator it = stack.iterator(); + while (it.hasNext()) { + Json.toJson(j, it.next(), c); + } + j.endArray(); + }, + (t, o, it, generics, context) -> { + Stack stack = (Stack)o; + for (ValueType vt : t.streamArray()){ + stack.add(Json.instantiateFromJson(t, (Class)generics[0], context)); + } + } + ); } - } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/JsonTraverser.java b/src/main/java/de/rwth/montisim/commons/utils/json/JsonTraverser.java similarity index 92% rename from src/main/java/de/rwth/montisim/commons/utils/JsonTraverser.java rename to src/main/java/de/rwth/montisim/commons/utils/json/JsonTraverser.java index 7d319fa..f317c85 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/JsonTraverser.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/JsonTraverser.java @@ -1,13 +1,17 @@ -package de.rwth.montisim.commons.utils; +package de.rwth.montisim.commons.utils.json; import java.io.*; import java.util.Iterator; +import de.rwth.montisim.commons.utils.Pair; +import de.rwth.montisim.commons.utils.ParsingException; +import de.rwth.montisim.commons.utils.StringRef; +import de.rwth.montisim.commons.utils.json.Json; + /** * @throws ParsingException */ public class JsonTraverser { - public static final String K_TYPE = "type"; private static final int PRE_DEPTH = 20; char[] data; @@ -63,6 +67,24 @@ public class JsonTraverser { return currentType; } + private char nextNonWS(){ + int p = pos; + char nc = '\0'; + do { + p++; + nc = p >= data.length ? '\0' : data[p]; + } while(nc == ' ' || nc == '\n' || nc == '\r' || nc == '\t'); + return nc; + } + + public boolean isEmpty(){ + if (currentType == ValueType.OBJECT){ + return nextNonWS() == '}'; + } else if (currentType == ValueType.ARRAY) { + return nextNonWS() == ']'; + } else return false; + } + public ObjectIterable streamObject() throws ParsingException { if (currentType != ValueType.OBJECT) throw new ParsingException(data, pos, "Tried to read OBJECT but got "+currentType); nextChar(); // Move after '{' @@ -146,20 +168,20 @@ public class JsonTraverser { public Pair getStructureType() throws ParsingException { ObjectIterable it = streamObject(); - if (!it.iterator().hasNext()) expected(K_TYPE); + if (!it.iterator().hasNext()) expected(Json.K_TYPE); Entry e = it.iterator().next(); - if (!e.key.getJsonString().equals(K_TYPE)) expected(K_TYPE); + if (!e.key.getJsonString().equals(Json.K_TYPE)) expected(Json.K_TYPE); String type = getString().getJsonString(); return new Pair(type, it); } public ObjectIterable expectStructureType(String type) throws ParsingException { ObjectIterable it = streamObject(); - if (!it.iterator().hasNext()) expected(K_TYPE); + if (!it.iterator().hasNext()) expected(Json.K_TYPE); Entry e = it.iterator().next(); - if (!e.key.getJsonString().equals(K_TYPE)) expected(K_TYPE); - String t = getString().getJsonString(); - if (!type.equals(t)) expectedStructureType(type, t); + if (!e.key.getJsonString().equals(Json.K_TYPE)) expected(Json.K_TYPE); + StringRef t = getString(); + if (!t.equals(type)) expectedStructureType(type, t.getRawString()); return it; } diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/Subtyped.java b/src/main/java/de/rwth/montisim/commons/utils/json/JsonType.java similarity index 67% rename from src/main/java/de/rwth/montisim/commons/utils/json/Subtyped.java rename to src/main/java/de/rwth/montisim/commons/utils/json/JsonType.java index eba5db4..d06cca0 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/json/Subtyped.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/JsonType.java @@ -4,6 +4,6 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) -public @interface Subtyped { - +public @interface JsonType { + Type value() default Type.OBJECT; } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/JsonTyped.java b/src/main/java/de/rwth/montisim/commons/utils/json/JsonTyped.java deleted file mode 100644 index 563aa6c..0000000 --- a/src/main/java/de/rwth/montisim/commons/utils/json/JsonTyped.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.rwth.montisim.commons.utils.json; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface JsonTyped { - String value() default ""; -} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/JsonWriter.java b/src/main/java/de/rwth/montisim/commons/utils/json/JsonWriter.java similarity index 96% rename from src/main/java/de/rwth/montisim/commons/utils/JsonWriter.java rename to src/main/java/de/rwth/montisim/commons/utils/json/JsonWriter.java index 1bbc20b..812a7b8 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/JsonWriter.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/JsonWriter.java @@ -1,86 +1,16 @@ -package de.rwth.montisim.commons.utils; +package de.rwth.montisim.commons.utils.json; import java.io.IOException; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.JsonTraverser.*; public class JsonWriter { - public static void main(String args[]) throws IOException { - JsonWriter writer = new JsonWriter(); - writer.format = true; - jsonTest(writer); - - String s1 = writer.getString(); - - writer.format = false; - jsonTest(writer); - - String s2 = writer.getString(); - - JsonTraverser j = new JsonTraverser(); - j.init(s1); - writer.init(); - rewrite(writer, j); - - String s3 = writer.getString(); - - System.out.println("Re-written"); - System.out.println(s3); + public JsonWriter(boolean format){ + init(); + this.format = format; } - public static void jsonTest(JsonWriter writer){ - writer.init(); - - writer.startObject(); - writer.writeKey("firstKey"); - writer.writeValue(false); - writer.writeKey("sec Key"); - writer.writeValue(3); - writer.writeKey("t Key"); - writer.writeValue(4.2); - writer.writeKey("f Key"); - writer.writeValue("a string \n \t \r \b \f \" \\ /"); - writer.writeKey("NaN"); - writer.writeValue(Double.NaN); - writer.writeKey("+Inf"); - writer.writeValue(Double.POSITIVE_INFINITY); - writer.writeKey("-Inf"); - writer.writeValue(Double.NEGATIVE_INFINITY); - - writer.writeKey("Nested Object"); - writer.startObject(); - writer.writeKey("a"); - writer.writeValue("b"); - writer.endObject(); - - writer.writeKey("Empty Object"); - writer.startObject(); - writer.endObject(); - - writer.writeKey("Array"); - writer.startArray(); - writer.writeValue("str"); - writer.writeValue(true); - writer.writeKey("Nested Object"); - writer.startObject(); - writer.writeKey("a"); - writer.writeValue("b"); - writer.endObject(); - writer.writeValue(true); - writer.endArray(); - - writer.writeKey("Empty array"); - writer.startArray(); - writer.endArray(); - - writer.endObject(); - - System.out.println(writer.getString()); - } - - public static final int TAB = 2; public StringBuilder res; public boolean format = true; @@ -287,4 +217,81 @@ public class JsonWriter { break; } } + + + + + + + public static void main(String args[]) throws IOException { + JsonWriter writer = new JsonWriter(true); + jsonTest(writer); + + String s1 = writer.getString(); + + writer.format = false; + jsonTest(writer); + + String s2 = writer.getString(); + + JsonTraverser j = new JsonTraverser(); + j.init(s1); + writer.init(); + rewrite(writer, j); + + String s3 = writer.getString(); + + System.out.println("Re-written"); + System.out.println(s3); + } + + public static void jsonTest(JsonWriter writer){ + writer.init(); + + writer.startObject(); + writer.writeKey("firstKey"); + writer.writeValue(false); + writer.writeKey("sec Key"); + writer.writeValue(3); + writer.writeKey("t Key"); + writer.writeValue(4.2); + writer.writeKey("f Key"); + writer.writeValue("a string \n \t \r \b \f \" \\ /"); + writer.writeKey("NaN"); + writer.writeValue(Double.NaN); + writer.writeKey("+Inf"); + writer.writeValue(Double.POSITIVE_INFINITY); + writer.writeKey("-Inf"); + writer.writeValue(Double.NEGATIVE_INFINITY); + + writer.writeKey("Nested Object"); + writer.startObject(); + writer.writeKey("a"); + writer.writeValue("b"); + writer.endObject(); + + writer.writeKey("Empty Object"); + writer.startObject(); + writer.endObject(); + + writer.writeKey("Array"); + writer.startArray(); + writer.writeValue("str"); + writer.writeValue(true); + writer.writeKey("Nested Object"); + writer.startObject(); + writer.writeKey("a"); + writer.writeValue("b"); + writer.endObject(); + writer.writeValue(true); + writer.endArray(); + + writer.writeKey("Empty array"); + writer.startArray(); + writer.endArray(); + + writer.endObject(); + + System.out.println(writer.getString()); + } } \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/Jsonable.java b/src/main/java/de/rwth/montisim/commons/utils/json/Jsonable.java deleted file mode 100644 index 1e83fcf..0000000 --- a/src/main/java/de/rwth/montisim/commons/utils/json/Jsonable.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.rwth.montisim.commons.utils.json; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Jsonable { - StructureType type() default StructureType.OBJECT; - FieldSelect fields() default FieldSelect.EXPLICIT; - FieldOptional optional() default FieldOptional.EXPLICIT; -} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/Select.java b/src/main/java/de/rwth/montisim/commons/utils/json/Select.java new file mode 100644 index 0000000..beef705 --- /dev/null +++ b/src/main/java/de/rwth/montisim/commons/utils/json/Select.java @@ -0,0 +1,6 @@ +package de.rwth.montisim.commons.utils.json; + +public enum Select { + EXPLICIT, + ALL +} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/SerializationContext.java b/src/main/java/de/rwth/montisim/commons/utils/json/SerializationContext.java new file mode 100644 index 0000000..e955d15 --- /dev/null +++ b/src/main/java/de/rwth/montisim/commons/utils/json/SerializationContext.java @@ -0,0 +1,5 @@ +package de.rwth.montisim.commons.utils.json; + +public interface SerializationContext { + +} \ No newline at end of file diff --git a/src/main/java/de/rwth/montisim/commons/utils/json/StructureType.java b/src/main/java/de/rwth/montisim/commons/utils/json/Type.java similarity index 70% rename from src/main/java/de/rwth/montisim/commons/utils/json/StructureType.java rename to src/main/java/de/rwth/montisim/commons/utils/json/Type.java index 73a4b82..3d585e0 100644 --- a/src/main/java/de/rwth/montisim/commons/utils/json/StructureType.java +++ b/src/main/java/de/rwth/montisim/commons/utils/json/Type.java @@ -1,6 +1,6 @@ package de.rwth.montisim.commons.utils.json; -public enum StructureType { +public enum Type { OBJECT, ARRAY } \ No newline at end of file diff --git a/src/test/java/de/rwth/montisim/commons/utils/json/AnnotatedJsonTest.java b/src/test/java/de/rwth/montisim/commons/utils/json/AnnotatedJsonTest.java index e9de661..211e7f0 100644 --- a/src/test/java/de/rwth/montisim/commons/utils/json/AnnotatedJsonTest.java +++ b/src/test/java/de/rwth/montisim/commons/utils/json/AnnotatedJsonTest.java @@ -3,10 +3,14 @@ package de.rwth.montisim.commons.utils.json; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.util.Vector; import org.junit.Assert; import org.junit.Test; +import de.rwth.montisim.commons.utils.json.JsonTraverser.Entry; +import de.rwth.montisim.commons.utils.json.JsonTraverser.ObjectIterable; public class AnnotatedJsonTest { @@ -14,10 +18,7 @@ public class AnnotatedJsonTest { public void simpleTest1() throws IllegalArgumentException, IllegalAccessException { SimpleClass1 o = new SimpleClass1(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{\n" + " \"s1\": \"a string\",\n" + " \"double1\": 5.6\n" + "}", res); } @@ -26,10 +27,7 @@ public class AnnotatedJsonTest { public void simpleTest2() throws IllegalArgumentException, IllegalAccessException { SimpleClass2 o = new SimpleClass2(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{\n" + " \"s1\": \"abcde\",\n" + " \"i1\": 4,\n" + " \"d1\": 5.6,\n" + " \"aaa\": 4.2,\n" + " \"c1\": 65,\n" + " \"sh1\": 10,\n" + " \"l1\": -5,\n" + " \"b1\": 8,\n" @@ -41,10 +39,7 @@ public class AnnotatedJsonTest { public void emptyTest1() throws IllegalArgumentException, IllegalAccessException { EmptyClass1 o = new EmptyClass1(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{}", res); @@ -54,10 +49,7 @@ public class AnnotatedJsonTest { public void simpleTest3() throws IllegalArgumentException, IllegalAccessException { SimpleClass3 o = new SimpleClass3(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("[\n" + " \"text\",\n" + " 1,\n" + " 100.0\n" + "]", res); @@ -67,17 +59,15 @@ public class AnnotatedJsonTest { public void nestedTest1() throws IllegalArgumentException, IllegalAccessException { NestedClass1 o = new NestedClass1(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{\n" + " \"s1\": \"abcde\",\n" + " \"c1\": {\n" + " \"s1\": \"a string\",\n" + " \"double1\": 5.6\n" + " },\n" + " \"nested_entry\": {\n" + " \"s1\": \"abcde\",\n" + " \"i1\": 4,\n" + " \"d1\": 5.6,\n" + " \"aaa\": 4.2,\n" + " \"c1\": 65,\n" + " \"sh1\": 10,\n" + " \"l1\": -5,\n" + " \"b1\": 8,\n" + " \"z1\": true\n" + " },\n" + " \"e1\": {},\n" + " \"arraySubClass\": [\n" + " \"text\",\n" + " 1,\n" + " 100.0\n" - + " ],\n" + " \"z1\": true\n" + "}", res); + + " ],\n" + " \"z1\": true,\n" + " \"i1\": {\n" + " \"type\": \"ISubclass1\",\n" + + " \"f1\": -1,\n" + " \"f2\": -2.0\n" + " }\n" + "}", res); } @@ -85,10 +75,7 @@ public class AnnotatedJsonTest { public void typedTest1() throws IllegalArgumentException, IllegalAccessException { TypedClass1 o = new TypedClass1(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{\n" + " \"type\": \"TypedClass1\",\n" + " \"a\": 1,\n" + " \"b\": 2\n" + "}", res); } @@ -97,10 +84,7 @@ public class AnnotatedJsonTest { public void typedTest2() throws IllegalArgumentException, IllegalAccessException { TypedClass2 o = new TypedClass2(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals("{\n" + " \"type\": \"type2\",\n" + " \"a\": 1,\n" + " \"b\": 2\n" + "}", res); } @@ -109,10 +93,7 @@ public class AnnotatedJsonTest { public void subclassTest1() throws IllegalArgumentException, IllegalAccessException { Subclass1 o = new Subclass1(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); + String res = Json.toFormattedJson(o); // System.out.println(res); Assert.assertEquals( "{\n" + " \"s1\": \"a string\",\n" + " \"double1\": 5.6,\n" + " \"a\": 1,\n" + " \"b\": 2\n" + "}", @@ -123,109 +104,275 @@ public class AnnotatedJsonTest { public void subclassTest2() throws IllegalArgumentException, IllegalAccessException { Subclass2 o = new Subclass2(); - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); - //System.out.println(res); + String res = Json.toFormattedJson(o); + // System.out.println(res); Assert.assertEquals("{\n" + " \"type\": \"sub2\",\n" + " \"s1\": \"a string\",\n" + " \"double1\": 5.6,\n" + " \"a\": 1,\n" + " \"b\": 2\n" + "}", res); } @Test - public void instanciateTest1() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + public void enumTest1() throws IllegalArgumentException, IllegalAccessException { + EnumClass1 o = new EnumClass1(); + + String res = Json.toFormattedJson(o); + //System.out.println(res); + Assert.assertEquals("{\n" + " \"f1\": \"VARIANT1\",\n" + " \"f2\": \"variant_2\"\n" + "}", res); + } + + @Test + public void instantiateTest1() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { SimpleClass1 o = new SimpleClass1(); o.set2(); // System.out.println("Constructors"); // for (Constructor c : SimpleClass1.class.getDeclaredConstructors()){ - // System.out.println(c); - // } - - Json j = Json.formattedWriter(); - j.toJson(o); + // System.out.println(c); + // } - String res = j.w.getString(); - //System.out.println(res); - Json j2 = Json.traverser(res); - SimpleClass1 o2 = j2.instanciateFromJson(SimpleClass1.class); + String res = Json.toFormattedJson(o); + // System.out.println(res); + SimpleClass1 o2 = Json.instantiateFromJson(res, SimpleClass1.class); o.assertEquals(o2); } @Test - public void instanciateTest2() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + public void instantiateTest2() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { SimpleClass2 o = new SimpleClass2(); o.set2(); - // System.out.println("Constructors"); - // for (Constructor c : SimpleClass1.class.getDeclaredConstructors()){ - // System.out.println(c); - // } - - Json j = Json.formattedWriter(); - j.toJson(o); - - String res = j.w.getString(); - //System.out.println(res); - Json j2 = Json.traverser(res); - SimpleClass2 o2 = j2.instanciateFromJson(SimpleClass2.class); + String res = Json.toFormattedJson(o); + // System.out.println(res); + SimpleClass2 o2 = Json.instantiateFromJson(res, SimpleClass2.class); o.assertEquals(o2); } @Test - public void instanciateTest3() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + public void instantiateTest3() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { SimpleClass3 o = new SimpleClass3(); o.set2(); // System.out.println("Fields"); // for (Field f : SimpleClass3.class.getDeclaredFields()){ - // System.out.println(f); - // } + // System.out.println(f); + // } - Json j = Json.formattedWriter(); - j.toJson(o); + String res = Json.toFormattedJson(o); + // System.out.println(res); + SimpleClass3 o2 = Json.instantiateFromJson(res, SimpleClass3.class); + o.assertEquals(o2); + } - String res = j.w.getString(); - //System.out.println(res); - Json j2 = Json.traverser(res); - SimpleClass3 o2 = j2.instanciateFromJson(SimpleClass3.class); + @Test + public void instantiateNestedTest() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + NestedClass1 o = new NestedClass1(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + NestedClass1 o2 = Json.instantiateFromJson(res, NestedClass1.class); o.assertEquals(o2); } @Test - public void instanciateNestedTest() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + public void instantiateTyped1Test() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + TypedClass1 o = new TypedClass1(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + TypedClass1 o2 = Json.instantiateFromJson(res, TypedClass1.class); + o.assertEquals(o2); + } + + @Test + public void instantiateTyped2Test() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + TypedClass2 o = new TypedClass2(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + TypedClass2 o2 = Json.instantiateFromJson(res, TypedClass2.class); + o.assertEquals(o2); + } + + @Test + public void instantiateSubclass1Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + Subclass1 o = new Subclass1(); + o.sset2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + Subclass1 o2 = Json.instantiateFromJson(res, Subclass1.class); + o.sassertEquals(o2); + } + + @Test + public void instantiateSubclass2Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + Subclass2 o = new Subclass2(); + o.sset2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + Subclass2 o2 = Json.instantiateFromJson(res, Subclass2.class); + o.sassertEquals(o2); + } + + @Test + public void instantiateSubtyped1Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + NestedClass2 o = new NestedClass2(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + NestedClass2 o2 = Json.instantiateFromJson(res, NestedClass2.class); + o.assertEquals(o2); + } + + @Test + public void instantiateSubtyped2Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + Subclass2 o = new Subclass2(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + SuperClass1 o2 = Json.instantiateFromJson(res, SuperClass1.class); + Assert.assertTrue(o2 instanceof Subclass2); + o.sassertEquals((Subclass2) o2); + } + + @Test + public void instantiateSubtypedInterface1Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + ISubclass1 o = new ISubclass1(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + Interface1 o2 = Json.instantiateFromJson(res, Interface1.class); + Assert.assertTrue(o2 instanceof ISubclass1); + o.assertEquals((ISubclass1) o2); + } + + @Test + public void testFromJson1() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + SimpleClass1 o = new SimpleClass1(); + Json.fromJson("{\"s1\": \"other string\"}", o); + + SimpleClass1 o2 = new SimpleClass1(); + o2.set3(); + o2.assertEquals(o); + } + + @Test + public void testFromJson2() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + NestedClass1 o = new NestedClass1(); + Json.fromJson("{\"s1\": \"xxxxxx\", \"c1\":{\"s1\": \"other string\"}, \"z1\": false }", o); + + NestedClass1 o2 = new NestedClass1(); + o2.set3(); + o2.assertEquals(o); + } + + @Test + public void testFromJson3() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + + NestedClass1 o = new NestedClass1(); + o.set4(); + Json.fromJson("{\"i1\":{\"type\":\"ISubclass1\", \"f1\": 42}}", o); + + NestedClass1 o2 = new NestedClass1(); + o2.set5(); + o2.assertEquals(o); + } + + @Test + public void testFromJson4() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + NestedClass1 o = new NestedClass1(); o.set2(); + Json.fromJson("{\"i1\":{\"type\":\"ISubclass1\", \"f1\": 42, \"f2\": -7}}", o); - // System.out.println("Constructors"); - // for (Constructor c : SimpleClass1.class.getDeclaredConstructors()){ - // System.out.println(c); - // } + NestedClass1 o2 = new NestedClass1(); + o2.set2(); + o2.set6(); + o2.assertEquals(o); + } - Json j = Json.formattedWriter(); - j.toJson(o); + @Test + public void customTest1() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException { - String res = j.w.getString(); - //System.out.println(res); - Json j2 = Json.traverser(res); - NestedClass1 o2 = j2.instanciateFromJson(NestedClass1.class); + CustomClass1 o = new CustomClass1(); + o.set2(); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + CustomClass1 o2 = Json.instantiateFromJson(res, CustomClass1.class); + o.assertEquals(o2); + } + + @Test + public void vectorTest1() throws InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException, IOException, NoSuchFieldException { + + VectorClass1 o = new VectorClass1(); + o.set2(); + + // Class c = o.getClass(); + // System.out.println("VectorClass1 class: "+c.toString()); + // Field f = c.getDeclaredField("vec"); + // System.out.println("Field vec: "+f.toGenericString()); + // ParameterizedType pt = (ParameterizedType) f.getGenericType(); + // System.out.println("ParamType: "+pt.toString()); + // Class genC = (Class) pt.getActualTypeArguments()[0]; + // System.out.println("actualType[0]: "+genC); + + String res = Json.toFormattedJson(o); + // System.out.println(res); + VectorClass1 o2 = Json.instantiateFromJson(res, VectorClass1.class); o.assertEquals(o2); } - // TODO Test "typed" + @Test + public void instantiateEnum1Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + EnumClass1 o = new EnumClass1(); + o.set2(); - // TODO test subclasses + String res = Json.toFormattedJson(o); + // System.out.println(res); + EnumClass1 o2 = Json.instantiateFromJson(res, EnumClass1.class); + o.assertEquals(o2); + } - // TODO test typed subclasses + @Test + public void instantiateEnum2Test() throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { + EnumClass1 o = new EnumClass1(); + o.set2(); - // TODO test instanciate from super type into subtype variants + String res = Json.toFormattedJson(o); + // System.out.println(res); + EnumClass1 o2 = Json.instantiateFromJson(res, EnumClass1.class); + o.assertEquals(o2); + } } -@Jsonable +@FieldSelect(Select.EXPLICIT) class SimpleClass1 { @JsonEntry String s1; @@ -233,7 +380,7 @@ class SimpleClass1 { @JsonEntry("double1") double d1; - SimpleClass1(){ + SimpleClass1() { set1(); } @@ -249,13 +396,16 @@ class SimpleClass1 { d1 = 1.1; } + void set3() { + s1 = "other string"; + } + public void assertEquals(SimpleClass1 o2) { Assert.assertEquals("s1", s1, o2.s1); Assert.assertEquals("d1", d1, o2.d1, 0.00001); } } -@Jsonable(fields=FieldSelect.ALL) class SimpleClass2 { String s1; int i1; @@ -263,18 +413,18 @@ class SimpleClass2 { @JsonEntry("aaa") float f1; char c1; - //@IgnoreField + // @IgnoreField transient String s2 = "chocolate"; short sh1; long l1; byte b1; boolean z1; - SimpleClass2(){ + SimpleClass2() { set1(); } - void set1(){ + void set1() { s1 = "abcde"; i1 = 4; d1 = 5.6; @@ -286,7 +436,7 @@ class SimpleClass2 { z1 = true; } - void set2(){ + void set2() { s1 = "zzzz"; i1 = 480; d1 = -8.8; @@ -311,29 +461,30 @@ class SimpleClass2 { } } -@Jsonable +@FieldSelect(Select.EXPLICIT) class EmptyClass1 { String s1; int i1; double d1; } -@Jsonable(fields=FieldSelect.ALL,type=StructureType.ARRAY) +@JsonType(Type.ARRAY) class SimpleClass3 { String s1; private int i1; double d1; - SimpleClass3(){ + SimpleClass3() { set1(); } - void set1(){ + void set1() { s1 = "text"; i1 = 1; d1 = 100; } - void set2(){ + + void set2() { s1 = "other text"; i1 = 78; d1 = -100.1; @@ -345,32 +496,33 @@ class SimpleClass3 { } } -@Jsonable(fields=FieldSelect.ALL) class NestedClass1 { String s1; SimpleClass1 c1; @JsonEntry("nested_entry") SimpleClass2 c2; - //@IgnoreField + // @IgnoreField transient SimpleClass2 c3; EmptyClass1 e1; SimpleClass3 arraySubClass; boolean z1; + Interface1 i1; - NestedClass1(){ + NestedClass1() { set1(); } - void set1(){ + void set1() { s1 = "abcde"; c1 = new SimpleClass1(); c2 = new SimpleClass2(); e1 = new EmptyClass1(); arraySubClass = new SimpleClass3(); z1 = true; + i1 = new ISubclass1(); } - void set2(){ + void set2() { s1 = "edcba"; c1 = new SimpleClass1(); c1.set2(); @@ -380,6 +532,33 @@ class NestedClass1 { arraySubClass = new SimpleClass3(); arraySubClass.set2(); z1 = false; + i1 = new ISubclass2(); + } + + void set3() { + s1 = "xxxxxx"; + c1.set3(); + z1 = false; + } + + void set4() { + ISubclass1 i = new ISubclass1(); + i1 = i; + i.set2(); + } + + void set5() { + ISubclass1 i = new ISubclass1(); + i1 = i; + i.set2(); + i.set3(); + } + + void set6() { + ISubclass1 i = new ISubclass1(); + i1 = i; + i.f1 = 42; + i.f2 = -7; } public void assertEquals(NestedClass1 o2) { @@ -388,60 +567,392 @@ class NestedClass1 { c2.assertEquals(o2.c2); arraySubClass.assertEquals(o2.arraySubClass); Assert.assertEquals("z1", z1, o2.z1); + Assert.assertTrue(i1.getClass().equals(o2.i1.getClass())); + if (i1 instanceof ISubclass1) { + ((ISubclass1) i1).assertEquals((ISubclass1) o2.i1); + } else { + ((ISubclass2) i1).assertEquals((ISubclass2) o2.i1); + } } } -@Jsonable(fields=FieldSelect.ALL) @Typed class TypedClass1 { int a; int b; - TypedClass1(){ + + TypedClass1() { set1(); } - void set1(){ + + void set1() { a = 1; b = 2; } + + void set2() { + a = 3; + b = 4; + } + + public void assertEquals(TypedClass1 o2) { + Assert.assertEquals("a", a, o2.a); + Assert.assertEquals("b", b, o2.b); + } } -@Jsonable(fields=FieldSelect.ALL) @Typed("type2") class TypedClass2 { int a; int b; - TypedClass2(){ + + TypedClass2() { set1(); } - void set1(){ + + void set1() { a = 1; b = 2; } + + void set2() { + a = 3; + b = 4; + } + + public void assertEquals(TypedClass2 o2) { + Assert.assertEquals("a", a, o2.a); + Assert.assertEquals("b", b, o2.b); + } } -@Jsonable(fields=FieldSelect.ALL) class Subclass1 extends SimpleClass1 { int a; int b; - Subclass1(){ + + Subclass1() { sset1(); } - void sset1(){ + + void sset1() { + set1(); a = 1; b = 2; } + + void sset2() { + set2(); + a = 3; + b = 4; + } + + public void sassertEquals(Subclass1 o2) { + assertEquals(o2); + Assert.assertEquals("a", a, o2.a); + Assert.assertEquals("b", b, o2.b); + } +} + +@FieldSelect(Select.EXPLICIT) +@Typed +class SuperClass1 { + @JsonEntry + String s1; + int i1; // Is NOT serialized + @JsonEntry("double1") + double d1; + + SuperClass1() { + set1(); + } + + void set1() { + s1 = "a string"; + i1 = 4; + d1 = 5.6; + } + + void set2() { + s1 = "doubidou"; + i1 = 4; + d1 = 1.1; + } + + public void assertEquals(SuperClass1 o2) { + Assert.assertEquals("s1", s1, o2.s1); + Assert.assertEquals("d1", d1, o2.d1, 0.00001); + } } -@Jsonable(fields=FieldSelect.ALL) @Typed("sub2") -class Subclass2 extends SimpleClass1 { +class Subclass2 extends SuperClass1 { int a; int b; - Subclass2(){ + + Subclass2() { sset1(); } - void sset1(){ + + void sset1() { + set1(); a = 1; b = 2; } + + void sset2() { + set2(); + a = 3; + b = 4; + } + + public void sassertEquals(Subclass2 o2) { + assertEquals(o2); + Assert.assertEquals("a", a, o2.a); + Assert.assertEquals("b", b, o2.b); + } +} + +@Typed() +class Subclass3 extends SuperClass1 { + static { + try { + Json.registerType(Subclass3.class); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + int c; + int d; + + Subclass3() { + sset1(); + } + + void sset1() { + set1(); + c = -1; + d = -2; + } + + void sset2() { + set2(); + c = -3; + d = -4; + } + + public void sassertEquals(Subclass3 o2) { + assertEquals(o2); + Assert.assertEquals("c", c, o2.c); + Assert.assertEquals("d", d, o2.d); + } +} + +@Typed() +class NestedClass2 { + SuperClass1 c; + + NestedClass2() { + set1(); + } + + void set1() { + c = new Subclass2(); + } + + void set2() { + c = new Subclass3(); + } + + public void assertEquals(NestedClass2 o2) { + Assert.assertEquals("Missmatching classes", c.getClass(), o2.c.getClass()); + if (c instanceof Subclass2) { + ((Subclass2) c).sassertEquals((Subclass2) o2.c); + } else if (c instanceof Subclass3) { + ((Subclass3) c).sassertEquals((Subclass3) o2.c); + } else { + throw new IllegalArgumentException("Unknown sub-instance"); + } + } +} + +interface Interface1 { + void someFunc(); +} + +@Typed +class ISubclass1 implements Interface1 { + static { + try { + Json.registerType(ISubclass1.class); + } catch (IllegalArgumentException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + int f1; + double f2; + + @Override + public void someFunc() { + } + + ISubclass1() { + set1(); + } + + void set1() { + f1 = -1; + f2 = -2; + } + + void set2() { + f1 = -3; + f2 = -4; + } + + void set3() { + f1 = 42; + } + + public void assertEquals(ISubclass1 o2) { + Assert.assertEquals("f1", f1, o2.f1); + Assert.assertEquals("f2", f2, o2.f2, 0.00001); + } +} + +@Typed +class ISubclass2 implements Interface1 { + static { + try { + Json.registerType(ISubclass2.class); + } catch (IllegalArgumentException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + String f3; + short f4; + + @Override + public void someFunc() { + } + + ISubclass2() { + set1(); + } + + void set1() { + f3 = "1"; + f4 = 2; + } + + void set2() { + f3 = "3"; + f4 = 4; + } + + public void assertEquals(ISubclass2 o2) { + Assert.assertEquals("f3", f3, o2.f3); + Assert.assertEquals("f4", f4, o2.f4); + } +} + +class CustomClass1 implements CustomJson { + NestedClass1 n1 = new NestedClass1(); + int o1 = 1; + + void set2(){ + n1.c1.d1 = 1.1; + o1 = 2; + } + + @Override + public void write(JsonWriter w, SerializationContext context) throws IllegalAccessException { + w.startObject(); + w.write("o1 custom", o1); + w.write("subelem", n1.c1.d1); + w.endObject(); + } + + @Override + public void read(JsonTraverser t, ObjectIterable it, SerializationContext context) + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + if (it == null) it = t.streamObject(); + for (Entry e : it) { + if (e.key.equals("o1 custom")) o1 = (int) t.getLong(); + else if (e.key.equals("subelem")) n1.c1.d1 = t.getDouble(); + else t.unexpected(e); + } + } + + void assertEquals(CustomClass1 o2){ + Assert.assertEquals("o1", o1, o2.o1); + n1.assertEquals(o2.n1); + } + +} + +class VectorClass1 { + Vector vec = new Vector<>(); + int o1 = 1; + VectorClass1() { + set1(); + } + + void set1() { + vec.clear(); + vec.add(new SimpleClass1()); + SimpleClass1 s2 = new SimpleClass1(); + s2.set2(); + vec.add(s2); + SimpleClass1 s3 = new SimpleClass1(); + vec.add(s3); + } + + void set2(){ + vec.clear(); + SimpleClass1 s1 = new SimpleClass1(); + s1.set3(); + vec.add(s1); + SimpleClass1 s2 = new SimpleClass1(); + s2.set1(); + vec.add(s2); + o1 = 2; + } + + void assertEquals(VectorClass1 o2){ + Assert.assertEquals("o1", o1, o2.o1); + Assert.assertEquals("vec length", vec.size(), o2.vec.size()); + for (int i = 0; i < vec.size(); ++i){ + vec.elementAt(i).assertEquals(o2.vec.elementAt(i)); + } + } + +} + +enum Enum1 { + VARIANT1, + @JsonEntry("variant_2") + VARIANT2, + VARIANT3 +} + +class EnumClass1 { + Enum1 f1; + Enum1 f2; + EnumClass1(){ + set1(); + } + void set1(){ + f1 = Enum1.VARIANT1; + f2 = Enum1.VARIANT2; + } + void set2(){ + f1 = Enum1.VARIANT3; + f2 = Enum1.VARIANT1; + } + void assertEquals(EnumClass1 o2){ + Assert.assertEquals("f1", f1, o2.f1); + Assert.assertEquals("f2", f2, o2.f2); + } } \ No newline at end of file diff --git a/src/test/java/de/rwth/montisim/commons/utils/JsonTest.java b/src/test/java/de/rwth/montisim/commons/utils/json/JsonTest.java similarity index 97% rename from src/test/java/de/rwth/montisim/commons/utils/JsonTest.java rename to src/test/java/de/rwth/montisim/commons/utils/json/JsonTest.java index f9779ca..4bf01c4 100644 --- a/src/test/java/de/rwth/montisim/commons/utils/JsonTest.java +++ b/src/test/java/de/rwth/montisim/commons/utils/json/JsonTest.java @@ -1,4 +1,4 @@ -package de.rwth.montisim.commons.utils; +package de.rwth.montisim.commons.utils.json; import java.io.IOException; import java.lang.annotation.Annotation; @@ -10,8 +10,8 @@ import org.junit.Test; import de.rwth.montisim.commons.dynamicinterface.DataType; import de.rwth.montisim.commons.simulation.DynamicObject; -import de.rwth.montisim.commons.utils.JsonTraverser.Entry; -import de.rwth.montisim.commons.utils.JsonTraverser.ValueType; +import de.rwth.montisim.commons.utils.json.JsonTraverser.Entry; +import de.rwth.montisim.commons.utils.json.JsonTraverser.ValueType; public class JsonTest { public static final String formatted = String.join("\n", "{", " \"firstKey\": false,", " \"sec Key\": 3,", @@ -24,7 +24,7 @@ public class JsonTest { @Test public void jsonWriterTest() { - JsonWriter writer = new JsonWriter(); + JsonWriter writer = new JsonWriter(true); writer.format = true; writeJsonSample(writer); -- GitLab