From: Daniel Kirschten Date: Fri, 30 Aug 2019 10:43:33 +0000 (+0200) Subject: Restructured JSON (de)serializing: ViewModels can be (de)serialized too X-Git-Url: https://mograsim.net/gitweb/?a=commitdiff_plain;h=853d979edee5d49d3b3c5fe08609f6cfd82d863f;p=Mograsim.git Restructured JSON (de)serializing: ViewModels can be (de)serialized too --- diff --git a/net.mograsim.logic.model.editor/src/net/mograsim/logic/model/editor/SaveLoadManager.java b/net.mograsim.logic.model.editor/src/net/mograsim/logic/model/editor/SaveLoadManager.java index 412313e9..1e3d2715 100644 --- a/net.mograsim.logic.model.editor/src/net/mograsim/logic/model/editor/SaveLoadManager.java +++ b/net.mograsim.logic.model.editor/src/net/mograsim/logic/model/editor/SaveLoadManager.java @@ -97,7 +97,8 @@ public class SaveLoadManager fdShell.dispose(); if (result != null) { - new Editor((DeserializedSubmodelComponent) SubmodelComponentSerializer.deserialize(new ViewModelModifiable(), result)); + new Editor((DeserializedSubmodelComponent) IndirectGUIComponentCreator.createComponent(new ViewModelModifiable(), + "file:" + result)); } } } diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/IndirectGUIComponentCreator.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/IndirectGUIComponentCreator.java index 2b747b2a..6ff8adb1 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/IndirectGUIComponentCreator.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/IndirectGUIComponentCreator.java @@ -9,6 +9,7 @@ import java.util.Map; import com.google.gson.JsonElement; import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import net.mograsim.logic.model.model.ViewModelModifiable; import net.mograsim.logic.model.model.components.GUIComponent; @@ -102,7 +103,14 @@ public class IndirectGUIComponentCreator throw new IllegalArgumentException("Can't give params to a component deserialized from a JSON file"); try { - return SubmodelComponentSerializer.deserialize(model, resolvedID.substring(5), name, id, null); + String filename = resolvedID.substring(5); + JsonObject jsonContents = JsonHandler.readJson(filename, JsonObject.class); + SerializablePojo jsonContentsAsSerializablePojo = JsonHandler.parser.fromJson(jsonContents, SerializablePojo.class); + if (jsonContentsAsSerializablePojo.version == null) + return LegacySubmodelComponentSerializer.deserialize(model, + JsonHandler.parser.fromJson(jsonContents, LegacySubmodelComponentParams.class), name, id, null); + return SubmodelComponentSerializer.deserialize(model, + JsonHandler.parser.fromJson(jsonContents, SubmodelComponentParams.class), name, id, null); } catch (IOException e) { diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentParams.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentParams.java new file mode 100644 index 00000000..c21fa866 --- /dev/null +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentParams.java @@ -0,0 +1,87 @@ +package net.mograsim.logic.model.serializing; + +import java.io.IOException; + +import com.google.gson.JsonElement; + +import net.haspamelodica.swt.helper.swtobjectwrappers.Point; +import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; +import net.mograsim.logic.model.util.JsonHandler; + +/** + * This class contains all the information necessary to create a new {@link SubmodelComponent} + */ +public class LegacySubmodelComponentParams +{ + // basic stuff + public double width, height; + public LegacyInterfacePinParams[] interfacePins; + public LegacySubmodelParameters submodel; + + // functionality that needs to be expressed in Java code + public String symbolRendererSnippetID; + public JsonElement symbolRendererParams; + + public String outlineRendererSnippetID; + public JsonElement outlineRendererParams; + + public String highLevelStateHandlerSnippetID; + public JsonElement highLevelStateHandlerParams; + + public static class LegacyInterfacePinParams + { + public Point location; + public String name; + public int logicWidth; + } + + public static class LegacySubmodelParameters + { + public double innerScale; + public LegacyInnerComponentParams[] subComps; + public LegacyInnerWireParams[] innerWires; + + public static class LegacyInnerComponentParams + { + public String id; + public String name; + public Point pos; + public JsonElement params; + } + + public static class LegacyInnerWireParams + { + public LegacyInnerPinParams pin1, pin2; + public String name; + public Point[] path; + + public static class LegacyInnerPinParams + { + public String compName; + public String pinName; + } + } + } + + public static LegacySubmodelComponentParams readJson(String path) throws IOException + { + return JsonHandler.readJson(path, LegacySubmodelComponentParams.class); + } + + /** + * Writes this {@link LegacySubmodelComponentParams} object into a file in json format. The correct file extension is important! Check + * {@link LegacySubmodelComponentParams}.fileExtension + */ + public void writeJson(String path) + { + try + { + JsonHandler.writeJson(this, path); + } + catch (IOException e) + { + System.err.println("Failed to write SubComponentParams to file"); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentSerializer.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentSerializer.java new file mode 100644 index 00000000..2ede422c --- /dev/null +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/LegacySubmodelComponentSerializer.java @@ -0,0 +1,312 @@ +package net.mograsim.logic.model.serializing; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import com.google.gson.JsonElement; + +import net.haspamelodica.swt.helper.swtobjectwrappers.Point; +import net.mograsim.logic.model.model.ViewModelModifiable; +import net.mograsim.logic.model.model.components.GUIComponent; +import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; +import net.mograsim.logic.model.model.wires.GUIWire; +import net.mograsim.logic.model.model.wires.MovablePin; +import net.mograsim.logic.model.model.wires.Pin; +import net.mograsim.logic.model.serializing.LegacySubmodelComponentParams.LegacyInterfacePinParams; +import net.mograsim.logic.model.serializing.LegacySubmodelComponentParams.LegacySubmodelParameters; +import net.mograsim.logic.model.serializing.LegacySubmodelComponentParams.LegacySubmodelParameters.LegacyInnerComponentParams; +import net.mograsim.logic.model.serializing.LegacySubmodelComponentParams.LegacySubmodelParameters.LegacyInnerWireParams; +import net.mograsim.logic.model.serializing.LegacySubmodelComponentParams.LegacySubmodelParameters.LegacyInnerWireParams.LegacyInnerPinParams; +import net.mograsim.logic.model.snippets.HighLevelStateHandler; +import net.mograsim.logic.model.snippets.Renderer; +import net.mograsim.logic.model.snippets.SubmodelComponentSnippetSuppliers; +import net.mograsim.logic.model.util.JsonHandler; + +/** + * Creates {@link SubmodelComponent}s from {@link LegacySubmodelComponentParams} + * + * @author Fabian Stemmler + * @author Daniel Kirschten + */ +public final class LegacySubmodelComponentSerializer +{ + // convenience methods + + /** + * Like {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams)}, but first reading the + * {@link LegacySubmodelComponentParams} from the given file path. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath) throws IOException + { + return deserialize(model, JsonHandler.readJson(sourcePath, LegacySubmodelComponentParams.class)); + } + + /** + * Like {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, JsonElement)}, but first reading the + * {@link LegacySubmodelComponentParams} from the given file path. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String idForSerializingOverride, + JsonElement paramsForSerializingOverride) throws IOException + { + return deserialize(model, JsonHandler.readJson(sourcePath, LegacySubmodelComponentParams.class), idForSerializingOverride, + paramsForSerializingOverride); + } + + /** + * Like {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String)}, but first reading the + * {@link LegacySubmodelComponentParams} from the given file path. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String name) throws IOException + { + return deserialize(model, JsonHandler.readJson(sourcePath, LegacySubmodelComponentParams.class), name); + } + + /** + * Like {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, String, JsonElement)}, but first reading the + * {@link LegacySubmodelComponentParams} from the given file path. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String name, String idForSerializingOverride, + JsonElement paramsForSerializingOverride) throws IOException + { + return deserialize(model, JsonHandler.readJson(sourcePath, LegacySubmodelComponentParams.class), name, idForSerializingOverride, + paramsForSerializingOverride); + } + + /** + * {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, String, JsonElement)} with no + * idForSerializingOverride set and using the default name. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, LegacySubmodelComponentParams params) + { + return deserialize(model, params, null, null, null); + } + + /** + * {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, String, JsonElement)} using the default name. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, LegacySubmodelComponentParams params, + String idForSerializingOverride, JsonElement paramsForSerializingOverride) + { + return deserialize(model, params, null, idForSerializingOverride, paramsForSerializingOverride); + } + + /** + * {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, String, JsonElement)} with no + * idForSerializingOverride set. + * + * @author Daniel Kirschten + */ + public static SubmodelComponent deserialize(ViewModelModifiable model, LegacySubmodelComponentParams params, String name) + { + return deserialize(model, params, name, null, null); + } + + /** + * Like {@link #serialize(SubmodelComponent)}, but instead of returning the generated {@link LegacySubmodelComponentParams} they are + * written to a file at the given path. + * + * @author Daniel Kirschten + */ + public static void serialize(SubmodelComponent comp, String targetPath) throws IOException + { + JsonHandler.writeJson(serialize(comp), targetPath); + } + + /** + * Like {@link #serialize(SubmodelComponent, Function)}, but instead of returning the generated {@link LegacySubmodelComponentParams} + * they are written to a file at the given path. + * + * @author Daniel Kirschten + */ + public static void serialize(SubmodelComponent comp, IdentifierGetter idGetter, String targetPath) throws IOException + { + JsonHandler.writeJson(serialize(comp, idGetter), targetPath); + } + + /** + * {@link #serialize(SubmodelComponent, Function)} using a default {@link IdentifierGetter} (see IdentifierGetter's + * {@link IdentifierGetter#IdentifierGetter() default constructor}) + * + * @author Daniel Kirschten + */ + public static LegacySubmodelComponentParams serialize(SubmodelComponent comp) + { + return serialize(comp, new IdentifierGetter()); + } + + // "core" methods + /** + * Creates a {@link SubmodelComponent} from the specified {@link LegacySubmodelComponentParams} with the given name. + *

+ * When serializing a SubmodelComponent, it is undesired for every subcomponent to be serialized with its complete inner + * structure. Instead, these sub-SubmodelComponents should be serialized with the ID and params which were used to + * determine the SubmodelComponentParams defining the sub-SubmodelComponent. Because of this, it is possible + * to override the ID and params used in {@link #serialize(SubmodelComponent, Function) serialize(...)} to describe this subcomponent. + * See there for details. + * + * @author Fabian Stemmler + * @author Daniel Kirschten + */ + @SuppressWarnings("unused") // for GUIWire being created + public static SubmodelComponent deserialize(ViewModelModifiable model, LegacySubmodelComponentParams params, String name, + String idForSerializingOverride, JsonElement paramsForSerializingOverride) + { + DeserializedSubmodelComponent comp = new DeserializedSubmodelComponent(model, name, idForSerializingOverride, + paramsForSerializingOverride); + comp.setSubmodelScale(params.submodel.innerScale); + comp.setSize(params.width, params.height); + for (LegacyInterfacePinParams iPinParams : params.interfacePins) + comp.addSubmodelInterface( + new MovablePin(comp, iPinParams.name, iPinParams.logicWidth, iPinParams.location.x, iPinParams.location.y)); + LegacySubmodelParameters submodelParams = params.submodel; + ViewModelModifiable submodelModifiable = comp.getSubmodelModifiable(); + Map componentsByName = submodelModifiable.getComponentsByName(); + GUIComponent[] components = new GUIComponent[submodelParams.subComps.length]; + for (int i = 0; i < components.length; i++) + { + LegacyInnerComponentParams cParams = submodelParams.subComps[i]; + components[i] = IndirectGUIComponentCreator.createComponent(submodelModifiable, cParams.id, cParams.params, cParams.name); + components[i].moveTo(cParams.pos.x, cParams.pos.y); + } + + for (int i = 0; i < submodelParams.innerWires.length; i++) + { + LegacyInnerWireParams innerWire = submodelParams.innerWires[i]; + new GUIWire(submodelModifiable, innerWire.name, componentsByName.get(innerWire.pin1.compName).getPin(innerWire.pin1.pinName), + componentsByName.get(innerWire.pin2.compName).getPin(innerWire.pin2.pinName), innerWire.path); + } + comp.setSymbolRenderer(SubmodelComponentSnippetSuppliers.symbolRendererSupplier.getSnippetSupplier(params.symbolRendererSnippetID) + .create(comp, params.symbolRendererParams)); + comp.setOutlineRenderer(SubmodelComponentSnippetSuppliers.outlineRendererSupplier + .getSnippetSupplier(params.outlineRendererSnippetID).create(comp, params.outlineRendererParams)); + comp.setHighLevelStateHandler(SubmodelComponentSnippetSuppliers.highLevelStateHandlerSupplier + .getSnippetSupplier(params.highLevelStateHandlerSnippetID).create(comp, params.highLevelStateHandlerParams)); + return comp; + } + + /** + * Returns {@link LegacySubmodelComponentParams}, which describe this {@link SubmodelComponent}.
+ * Subcomponents are serialized in the following way:
+ * If a subcomponent is a SubmodelComponent which has been deserialized, and it has an + * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set (e.g. non-null; see + * {@link #deserialize(ViewModelModifiable, LegacySubmodelComponentParams, String, String, JsonElement) deserialize(...)}), this ID and + * the component's {@link DeserializedSubmodelComponent#paramsForSerializingOverride paramsForSerializingOverride} are written.
+ * If this case doesn't apply (e.g. if the subcomponent is not a SubmodelComponent; or it is a + * SubmodelComponent, but hasn't been deserialized; or it has no + * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set), the ID defined by idGetter + * and the params obtained by {@link GUIComponent#getParamsForSerializing() getParams()} are written.
+ * CodeSnippets are serialized using the ID defined by idGetter and the params obtained by the respective + * getParamsForSerializing methods ({@link Renderer#getParamsForSerializing()}). + * + * @author Fabian Stemmler + * @author Daniel Kirschten + */ + public static LegacySubmodelComponentParams serialize(SubmodelComponent comp, IdentifierGetter idGetter) + { + LegacySubmodelParameters submodelParams = new LegacySubmodelParameters(); + submodelParams.innerScale = comp.getSubmodelScale(); + + Map components = new HashMap<>(comp.submodel.getComponentsByName()); + components.remove(SubmodelComponent.SUBMODEL_INTERFACE_NAME); + LegacyInnerComponentParams[] componentParams = new LegacyInnerComponentParams[components.size()]; + int i1 = 0; + for (GUIComponent innerComponent : components.values()) + { + LegacyInnerComponentParams innerComponentParams = new LegacyInnerComponentParams(); + componentParams[i1] = innerComponentParams; + innerComponentParams.pos = new Point(innerComponent.getPosX(), innerComponent.getPosY()); + DeserializedSubmodelComponent innerCompCasted; + if (innerComponent instanceof DeserializedSubmodelComponent + && (innerCompCasted = (DeserializedSubmodelComponent) innerComponent).idForSerializingOverride != null) + { + innerComponentParams.id = innerCompCasted.idForSerializingOverride; + innerComponentParams.params = innerCompCasted.paramsForSerializingOverride; + } else + { + innerComponentParams.id = idGetter.componentIDs.apply(innerComponent); + innerComponentParams.params = innerComponent.getParamsForSerializing(idGetter); + } + innerComponentParams.name = innerComponent.name; + i1++; + } + submodelParams.subComps = componentParams; + + Collection wires = comp.submodel.getWiresByName().values(); + LegacyInnerWireParams wireParams[] = new LegacyInnerWireParams[wires.size()]; + i1 = 0; + for (GUIWire innerWire : wires) + { + LegacyInnerWireParams innerWireParams = new LegacyInnerWireParams(); + wireParams[i1] = innerWireParams; + LegacyInnerPinParams pin1Params = new LegacyInnerPinParams(), pin2Params = new LegacyInnerPinParams(); + + pin1Params.pinName = innerWire.getPin1().name; + pin1Params.compName = innerWire.getPin1().component.name; + pin2Params.pinName = innerWire.getPin2().name; + pin2Params.compName = innerWire.getPin2().component.name; + innerWireParams.name = innerWire.name; + innerWireParams.pin1 = pin1Params; + innerWireParams.pin2 = pin2Params; + innerWireParams.path = innerWire.getPath(); + i1++; + } + submodelParams.innerWires = wireParams; + + LegacySubmodelComponentParams params = new LegacySubmodelComponentParams(); + params.submodel = submodelParams; + + params.width = comp.getWidth(); + params.height = comp.getHeight(); + + LegacyInterfacePinParams[] iPins = new LegacyInterfacePinParams[comp.getPins().size()]; + int i = 0; + for (Pin p : comp.getPins().values()) + { + LegacyInterfacePinParams iPinParams = new LegacyInterfacePinParams(); + iPins[i] = iPinParams; + iPinParams.location = p.getRelPos(); + iPinParams.name = p.name; + iPinParams.logicWidth = p.logicWidth; + i++; + } + params.interfacePins = iPins; + + Renderer symbolRenderer = comp.getSymbolRenderer(); + if (symbolRenderer != null) + { + params.symbolRendererSnippetID = idGetter.symbolRendererIDs.apply(symbolRenderer); + params.symbolRendererParams = symbolRenderer.getParamsForSerializingJSON(idGetter); + } + + Renderer outlineRenderer = comp.getOutlineRenderer(); + if (outlineRenderer != null) + { + params.outlineRendererSnippetID = idGetter.outlineRendererIDs.apply(outlineRenderer); + params.outlineRendererParams = outlineRenderer.getParamsForSerializingJSON(idGetter); + } + + HighLevelStateHandler highLevelStateHandler = comp.getHighLevelStateHandler(); + if (highLevelStateHandler != null) + { + params.highLevelStateHandlerSnippetID = idGetter.highLevelStateHandlerIDs.apply(highLevelStateHandler); + params.highLevelStateHandlerParams = highLevelStateHandler.getParamsForSerializingJSON(idGetter); + } + + return params; + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SerializablePojo.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SerializablePojo.java new file mode 100644 index 00000000..4cc94d76 --- /dev/null +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SerializablePojo.java @@ -0,0 +1,19 @@ +package net.mograsim.logic.model.serializing; + +import net.mograsim.logic.model.util.Version; + +/** + * This class is the superclass of all POJOs that can be serialized to JSON. + * + * @author Daniel Kirschten + * + */ +public class SerializablePojo +{ + public Version version; + + public SerializablePojo(Version version) + { + this.version = version; + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentParams.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentParams.java index d47b2d98..155b6583 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentParams.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentParams.java @@ -1,22 +1,21 @@ package net.mograsim.logic.model.serializing; -import java.io.IOException; - import com.google.gson.JsonElement; import net.haspamelodica.swt.helper.swtobjectwrappers.Point; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; -import net.mograsim.logic.model.util.JsonHandler; +import net.mograsim.logic.model.util.Version; /** * This class contains all the information necessary to create a new {@link SubmodelComponent} */ -public class SubmodelComponentParams +public class SubmodelComponentParams extends SerializablePojo { // basic stuff public double width, height; public InterfacePinParams[] interfacePins; - public SubmodelParameters submodel; + public double innerScale; + public ViewModelParams submodel; // functionality that needs to be expressed in Java code public String symbolRendererSnippetID; @@ -28,60 +27,15 @@ public class SubmodelComponentParams public String highLevelStateHandlerSnippetID; public JsonElement highLevelStateHandlerParams; + public SubmodelComponentParams(Version version) + { + super(version); + } + public static class InterfacePinParams { public Point location; public String name; public int logicWidth; } - - public static class SubmodelParameters - { - public double innerScale; - public InnerComponentParams[] subComps; - public InnerWireParams[] innerWires; - - public static class InnerComponentParams - { - public String id; - public String name; - public Point pos; - public JsonElement params; - } - - public static class InnerWireParams - { - public InnerPinParams pin1, pin2; - public String name; - public Point[] path; - - public static class InnerPinParams - { - public String compName; - public String pinName; - } - } - } - - public static SubmodelComponentParams readJson(String path) throws IOException - { - return JsonHandler.readJson(path, SubmodelComponentParams.class); - } - - /** - * Writes this {@link SubmodelComponentParams} object into a file in json format. The correct file extension is important! Check - * {@link SubmodelComponentParams}.fileExtension - */ - public void writeJson(String path) - { - try - { - JsonHandler.writeJson(this, path); - } - catch (IOException e) - { - System.err.println("Failed to write SubComponentParams to file"); - e.printStackTrace(); - } - } } \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentSerializer.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentSerializer.java index 077c591a..126e2aa8 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentSerializer.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/SubmodelComponentSerializer.java @@ -1,29 +1,19 @@ package net.mograsim.logic.model.serializing; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; import com.google.gson.JsonElement; -import net.haspamelodica.swt.helper.swtobjectwrappers.Point; import net.mograsim.logic.model.model.ViewModelModifiable; -import net.mograsim.logic.model.model.components.GUIComponent; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; -import net.mograsim.logic.model.model.wires.GUIWire; import net.mograsim.logic.model.model.wires.MovablePin; import net.mograsim.logic.model.model.wires.Pin; import net.mograsim.logic.model.serializing.SubmodelComponentParams.InterfacePinParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerComponentParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams; -import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams.InnerPinParams; import net.mograsim.logic.model.snippets.HighLevelStateHandler; import net.mograsim.logic.model.snippets.Renderer; import net.mograsim.logic.model.snippets.SubmodelComponentSnippetSuppliers; import net.mograsim.logic.model.util.JsonHandler; +import net.mograsim.logic.model.util.Version; /** * Creates {@link SubmodelComponent}s from {@link SubmodelComponentParams} @@ -33,6 +23,7 @@ import net.mograsim.logic.model.util.JsonHandler; */ public final class SubmodelComponentSerializer { + public static final Version CURRENT_JSON_VERSION = Version.parseSemver("0.1.4"); // convenience methods /** @@ -128,8 +119,8 @@ public final class SubmodelComponentSerializer } /** - * Like {@link #serialize(SubmodelComponent, Function)}, but instead of returning the generated {@link SubmodelComponentParams} they are - * written to a file at the given path. + * Like {@link #serialize(SubmodelComponent, IdentifierGetter)}, but instead of returning the generated {@link SubmodelComponentParams} + * they are written to a file at the given path. * * @author Daniel Kirschten */ @@ -139,7 +130,7 @@ public final class SubmodelComponentSerializer } /** - * {@link #serialize(SubmodelComponent, Function)} using a default {@link IdentifierGetter} (see IdentifierGetter's + * {@link #serialize(SubmodelComponent, IdentifierGetter)} using a default {@link IdentifierGetter} (see IdentifierGetter's * {@link IdentifierGetter#IdentifierGetter() default constructor}) * * @author Daniel Kirschten @@ -156,8 +147,8 @@ public final class SubmodelComponentSerializer * When serializing a SubmodelComponent, it is undesired for every subcomponent to be serialized with its complete inner * structure. Instead, these sub-SubmodelComponents should be serialized with the ID and params which were used to * determine the SubmodelComponentParams defining the sub-SubmodelComponent. Because of this, it is possible - * to override the ID and params used in {@link #serialize(SubmodelComponent, Function) serialize(...)} to describe this subcomponent. - * See there for details. + * to override the ID and params used in {@link #serialize(SubmodelComponent, IdentifierGetter) serialize(...)} to describe this + * subcomponent. See there for details. * * @author Fabian Stemmler * @author Daniel Kirschten @@ -168,28 +159,13 @@ public final class SubmodelComponentSerializer { DeserializedSubmodelComponent comp = new DeserializedSubmodelComponent(model, name, idForSerializingOverride, paramsForSerializingOverride); - comp.setSubmodelScale(params.submodel.innerScale); + comp.setSubmodelScale(params.innerScale); comp.setSize(params.width, params.height); for (InterfacePinParams iPinParams : params.interfacePins) comp.addSubmodelInterface( new MovablePin(comp, iPinParams.name, iPinParams.logicWidth, iPinParams.location.x, iPinParams.location.y)); - SubmodelParameters submodelParams = params.submodel; ViewModelModifiable submodelModifiable = comp.getSubmodelModifiable(); - Map componentsByName = submodelModifiable.getComponentsByName(); - GUIComponent[] components = new GUIComponent[submodelParams.subComps.length]; - for (int i = 0; i < components.length; i++) - { - InnerComponentParams cParams = submodelParams.subComps[i]; - components[i] = IndirectGUIComponentCreator.createComponent(submodelModifiable, cParams.id, cParams.params, cParams.name); - components[i].moveTo(cParams.pos.x, cParams.pos.y); - } - - for (int i = 0; i < submodelParams.innerWires.length; i++) - { - InnerWireParams innerWire = submodelParams.innerWires[i]; - new GUIWire(submodelModifiable, innerWire.name, componentsByName.get(innerWire.pin1.compName).getPin(innerWire.pin1.pinName), - componentsByName.get(innerWire.pin2.compName).getPin(innerWire.pin2.pinName), innerWire.path); - } + ViewModelSerializer.deserialize(comp.getSubmodelModifiable(), params.submodel); comp.setSymbolRenderer(SubmodelComponentSnippetSuppliers.symbolRendererSupplier.getSnippetSupplier(params.symbolRendererSnippetID) .create(comp, params.symbolRendererParams)); comp.setOutlineRenderer(SubmodelComponentSnippetSuppliers.outlineRendererSupplier @@ -201,15 +177,8 @@ public final class SubmodelComponentSerializer /** * Returns {@link SubmodelComponentParams}, which describe this {@link SubmodelComponent}.
- * Subcomponents are serialized in the following way:
- * If a subcomponent is a SubmodelComponent which has been deserialized, and it has an - * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set (e.g. non-null; see - * {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement) deserialize(...)}), this ID and the - * component's {@link DeserializedSubmodelComponent#paramsForSerializingOverride paramsForSerializingOverride} are written.
- * If this case doesn't apply (e.g. if the subcomponent is not a SubmodelComponent; or it is a - * SubmodelComponent, but hasn't been deserialized; or it has no - * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set), the ID defined by idGetter - * and the params obtained by {@link GUIComponent#getParamsForSerializing() getParams()} are written.
+ * See {@link ViewModelSerializer#serialize(net.mograsim.logic.model.model.ViewModel, IdentifierGetter) + * ViewModelSerializer.serialize(...)} for how subcomponents are serialized.
* CodeSnippets are serialized using the ID defined by idGetter and the params obtained by the respective * getParamsForSerializing methods ({@link Renderer#getParamsForSerializing()}). * @@ -218,57 +187,9 @@ public final class SubmodelComponentSerializer */ public static SubmodelComponentParams serialize(SubmodelComponent comp, IdentifierGetter idGetter) { - SubmodelParameters submodelParams = new SubmodelParameters(); - submodelParams.innerScale = comp.getSubmodelScale(); - - Map components = new HashMap<>(comp.submodel.getComponentsByName()); - components.remove(SubmodelComponent.SUBMODEL_INTERFACE_NAME); - InnerComponentParams[] componentParams = new InnerComponentParams[components.size()]; - int i1 = 0; - for (GUIComponent innerComponent : components.values()) - { - InnerComponentParams innerComponentParams = new InnerComponentParams(); - componentParams[i1] = innerComponentParams; - innerComponentParams.pos = new Point(innerComponent.getPosX(), innerComponent.getPosY()); - DeserializedSubmodelComponent innerCompCasted; - if (innerComponent instanceof DeserializedSubmodelComponent - && (innerCompCasted = (DeserializedSubmodelComponent) innerComponent).idForSerializingOverride != null) - { - innerComponentParams.id = innerCompCasted.idForSerializingOverride; - innerComponentParams.params = innerCompCasted.paramsForSerializingOverride; - } else - { - innerComponentParams.id = idGetter.componentIDs.apply(innerComponent); - innerComponentParams.params = innerComponent.getParamsForSerializing(idGetter); - } - innerComponentParams.name = innerComponent.name; - i1++; - } - submodelParams.subComps = componentParams; - - Collection wires = comp.submodel.getWiresByName().values(); - InnerWireParams wireParams[] = new InnerWireParams[wires.size()]; - i1 = 0; - for (GUIWire innerWire : wires) - { - InnerWireParams innerWireParams = new InnerWireParams(); - wireParams[i1] = innerWireParams; - InnerPinParams pin1Params = new InnerPinParams(), pin2Params = new InnerPinParams(); - - pin1Params.pinName = innerWire.getPin1().name; - pin1Params.compName = innerWire.getPin1().component.name; - pin2Params.pinName = innerWire.getPin2().name; - pin2Params.compName = innerWire.getPin2().component.name; - innerWireParams.name = innerWire.name; - innerWireParams.pin1 = pin1Params; - innerWireParams.pin2 = pin2Params; - innerWireParams.path = innerWire.getPath(); - i1++; - } - submodelParams.innerWires = wireParams; - - SubmodelComponentParams params = new SubmodelComponentParams(); - params.submodel = submodelParams; + SubmodelComponentParams params = new SubmodelComponentParams(CURRENT_JSON_VERSION); + params.innerScale = comp.getSubmodelScale(); + params.submodel = ViewModelSerializer.serialize(comp.submodel, idGetter); params.width = comp.getWidth(); params.height = comp.getHeight(); diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelParams.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelParams.java new file mode 100644 index 00000000..6f4f82c3 --- /dev/null +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelParams.java @@ -0,0 +1,38 @@ +package net.mograsim.logic.model.serializing; + +import com.google.gson.JsonElement; + +import net.haspamelodica.swt.helper.swtobjectwrappers.Point; +import net.mograsim.logic.model.util.Version; + +public class ViewModelParams extends SerializablePojo +{ + public ComponentParams[] components; + public WireParams[] wires; + + public ViewModelParams(Version version) + { + super(version); + } + + public static class ComponentParams + { + public String id; + public String name; + public Point pos; + public JsonElement params; + } + + public static class WireParams + { + public PinParams pin1, pin2; + public String name; + public Point[] path; + + public static class PinParams + { + public String compName; + public String pinName; + } + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelSerializer.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelSerializer.java new file mode 100644 index 00000000..ffd18c94 --- /dev/null +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ViewModelSerializer.java @@ -0,0 +1,186 @@ +package net.mograsim.logic.model.serializing; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.gson.JsonElement; + +import net.haspamelodica.swt.helper.swtobjectwrappers.Point; +import net.mograsim.logic.model.model.ViewModel; +import net.mograsim.logic.model.model.ViewModelModifiable; +import net.mograsim.logic.model.model.components.GUIComponent; +import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; +import net.mograsim.logic.model.model.wires.GUIWire; +import net.mograsim.logic.model.serializing.ViewModelParams.ComponentParams; +import net.mograsim.logic.model.serializing.ViewModelParams.WireParams; +import net.mograsim.logic.model.serializing.ViewModelParams.WireParams.PinParams; +import net.mograsim.logic.model.util.JsonHandler; +import net.mograsim.logic.model.util.Version; + +public class ViewModelSerializer +{ + public static final Version CURRENT_JSON_VERSION = Version.parseSemver("0.1.1"); + + // convenience methods + /** + * Like {@link #deserialize(ViewModelParams)}, but first reading the {@link ViewModelParams} from the given file path. + * + * @author Daniel Kirschten + */ + public static ViewModelModifiable deserialize(String sourcePath) throws IOException + { + return deserialize(JsonHandler.readJson(sourcePath, ViewModelParams.class)); + } + + /** + * Like {@link #deserialize(ViewModelModifiable, ViewModelParams)}, but first reading the {@link ViewModelParams} from the given file + * path. + * + * @author Daniel Kirschten + */ + public static void deserialize(ViewModelModifiable model, String sourcePath) throws IOException + { + deserialize(model, JsonHandler.readJson(sourcePath, ViewModelParams.class)); + } + + /** + * Like {@link #deserialize(ViewModelModifiable, ViewModelParams)}, but using a newly created {@link ViewModelModifiable}. + * + * @author Daniel Kirschten + */ + public static ViewModelModifiable deserialize(ViewModelParams params) + { + ViewModelModifiable model = new ViewModelModifiable(); + deserialize(model, params); + return model; + } + + /** + * Like {@link #serialize(ViewModel)}, but instead of returning the generated {@link ViewModelParams} they are written to a file at the + * given path. + * + * @author Daniel Kirschten + */ + public static void serialize(ViewModel model, String targetPath) throws IOException + { + JsonHandler.writeJson(serialize(model), targetPath); + } + + /** + * Like {@link #serialize(ViewModel, IdentifierGetter)}, but instead of returning the generated {@link ViewModelParams} they are written + * to a file at the given path. + * + * @author Daniel Kirschten + */ + public static void serialize(ViewModel model, IdentifierGetter idGetter, String targetPath) throws IOException + { + JsonHandler.writeJson(serialize(model, idGetter), targetPath); + } + + /** + * {@link #serialize(ViewModel, IdentifierGetter)} using a default {@link IdentifierGetter} (see IdentifierGetter's + * {@link IdentifierGetter#IdentifierGetter() default constructor}) + * + * @author Daniel Kirschten + */ + public static ViewModelParams serialize(ViewModel model) + { + return serialize(model, new IdentifierGetter()); + } + + // "core" methods + /** + * Deserializes components and wires from the specified {@link ViewModelParams} and adds them to the given {@link ViewModelModifiable}. + * + * @author Fabian Stemmler + * @author Daniel Kirschten + */ + @SuppressWarnings("unused") // for GUIWire being created + public static void deserialize(ViewModelModifiable model, ViewModelParams params) + { + Map componentsByName = model.getComponentsByName(); + GUIComponent[] components = new GUIComponent[params.components.length]; + for (int i = 0; i < components.length; i++) + { + ComponentParams compParams = params.components[i]; + components[i] = IndirectGUIComponentCreator.createComponent(model, compParams.id, compParams.params, compParams.name); + components[i].moveTo(compParams.pos.x, compParams.pos.y); + } + + for (int i = 0; i < params.wires.length; i++) + { + WireParams wire = params.wires[i]; + new GUIWire(model, wire.name, componentsByName.get(wire.pin1.compName).getPin(wire.pin1.pinName), + componentsByName.get(wire.pin2.compName).getPin(wire.pin2.pinName), wire.path); + } + } + + /** + * Returns {@link ViewModelModifiable}, which describe the components and wires in the given {@link ViewModel}.
+ * Components are serialized in the following way:
+ * If a component is a SubmodelComponent which has been deserialized, and it has an + * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set (e.g. non-null; see + * {@link SubmodelComponentSerializer#deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement) + * SubmodelComponentSerializer.deserialize(...)}), this ID and the component's + * {@link DeserializedSubmodelComponent#paramsForSerializingOverride paramsForSerializingOverride} are written.
+ * If this case doesn't apply (e.g. if the component is not a SubmodelComponent; or it is a SubmodelComponent, + * but hasn't been deserialized; or it has no {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} + * set), the ID defined by idGetter and the params obtained by {@link GUIComponent#getParamsForSerializing() getParams()} + * are written. + * + * @author Fabian Stemmler + * @author Daniel Kirschten + */ + public static ViewModelParams serialize(ViewModel model, IdentifierGetter idGetter) + { + ViewModelParams modelParams = new ViewModelParams(CURRENT_JSON_VERSION); + + Map components = new HashMap<>(model.getComponentsByName()); + components.remove(SubmodelComponent.SUBMODEL_INTERFACE_NAME); + Set componentsParams = new HashSet<>(); + for (GUIComponent component : components.values()) + { + ComponentParams compParams = new ComponentParams(); + componentsParams.add(compParams); + compParams.pos = new Point(component.getPosX(), component.getPosY()); + DeserializedSubmodelComponent innerCompCasted; + if (component instanceof DeserializedSubmodelComponent + && (innerCompCasted = (DeserializedSubmodelComponent) component).idForSerializingOverride != null) + { + compParams.id = innerCompCasted.idForSerializingOverride; + compParams.params = innerCompCasted.paramsForSerializingOverride; + } else + { + compParams.id = idGetter.componentIDs.apply(component); + compParams.params = component.getParamsForSerializing(idGetter); + } + compParams.name = component.name; + } + modelParams.components = componentsParams.toArray(ComponentParams[]::new); + + Collection wires = model.getWiresByName().values(); + Set wiresParams = new HashSet<>(); + for (GUIWire innerWire : wires) + { + WireParams innerWireParams = new WireParams(); + wiresParams.add(innerWireParams); + PinParams pin1Params = new PinParams(), pin2Params = new PinParams(); + + pin1Params.pinName = innerWire.getPin1().name; + pin1Params.compName = innerWire.getPin1().component.name; + pin2Params.pinName = innerWire.getPin2().name; + pin2Params.compName = innerWire.getPin2().component.name; + innerWireParams.name = innerWire.name; + innerWireParams.pin1 = pin1Params; + innerWireParams.pin2 = pin2Params; + innerWireParams.path = innerWire.getPath(); + } + modelParams.wires = wiresParams.toArray(WireParams[]::new); + + return modelParams; + } +} \ No newline at end of file diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/util/JsonHandler.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/util/JsonHandler.java index 8dffc940..4f60bdfd 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/util/JsonHandler.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/util/JsonHandler.java @@ -14,8 +14,7 @@ import com.google.gson.JsonElement; public class JsonHandler { - // TODO: write versions differently - private static Gson parser = new GsonBuilder().setPrettyPrinting().create(); + public final static Gson parser = new GsonBuilder().setPrettyPrinting().create(); public static T readJson(String path, Class type) throws IOException { @@ -38,7 +37,7 @@ public class JsonHandler public static T fromJson(String src, Class type) { - // TODO actually parse and compare version + // throw away legacy version line String rawJson = src.lines().dropWhile(s -> s.length() == 0 || s.charAt(0) != '{').collect(Collectors.joining()); return parser.fromJson(rawJson, type); } @@ -53,7 +52,7 @@ public class JsonHandler public static String toJson(Object o) { - return String.format("mograsim version: %s\n%s", Version.jsonCompVersion.toString(), parser.toJson(o)); + return parser.toJson(o); } public static JsonElement toJsonTree(Object o) diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/util/Version.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/util/Version.java index b0a72982..ac42e13a 100644 --- a/net.mograsim.logic.model/src/net/mograsim/logic/model/util/Version.java +++ b/net.mograsim.logic.model/src/net/mograsim/logic/model/util/Version.java @@ -1,8 +1,17 @@ package net.mograsim.logic.model.util; +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import net.mograsim.logic.model.util.Version.VersionJSONAdapter; + +@JsonAdapter(VersionJSONAdapter.class) public final class Version { - public final static Version jsonCompVersion = new Version(0, 1, 3); public final int major, minor, patch; public Version(int major, int minor, int patch) @@ -20,10 +29,22 @@ public final class Version @Override public String toString() + { + return toSemverString(); + } + + public String toSemverString() { return major + "." + minor + "." + patch; } + public static Version parseSemver(String semver) + { + String[] semverParts = semver.split("\\."); + return new Version(Integer.parseInt(semverParts[0]), semverParts.length > 1 ? Integer.parseInt(semverParts[1]) : 0, + semverParts.length > 2 ? Integer.parseInt(semverParts[2]) : 0); + } + @Override public int hashCode() { @@ -54,7 +75,7 @@ public final class Version public boolean is(int major) { - return major != this.major; + return major == this.major; } public boolean is(int major, int minor) @@ -66,4 +87,19 @@ public final class Version { return is(major, minor) && this.patch == patch; } + + static class VersionJSONAdapter extends TypeAdapter + { + @Override + public void write(JsonWriter out, Version value) throws IOException + { + out.value(value.toSemverString()); + } + + @Override + public Version read(JsonReader in) throws IOException + { + return parseSemver(in.nextString()); + } + } } \ No newline at end of file