Merge remote-tracking branch 'origin/development' into development
[Mograsim.git] / net.mograsim.logic.model / src / net / mograsim / logic / model / serializing / SubmodelComponentSerializer.java
1 package net.mograsim.logic.model.serializing;
2
3 import java.io.IOException;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.function.Function;
8
9 import com.google.gson.Gson;
10 import com.google.gson.JsonElement;
11
12 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
13 import net.mograsim.logic.model.model.ViewModelModifiable;
14 import net.mograsim.logic.model.model.components.GUIComponent;
15 import net.mograsim.logic.model.model.components.submodels.SimpleRectangularSubmodelComponent;
16 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
17 import net.mograsim.logic.model.model.wires.GUIWire;
18 import net.mograsim.logic.model.model.wires.MovablePin;
19 import net.mograsim.logic.model.model.wires.Pin;
20 import net.mograsim.logic.model.serializing.SubmodelComponentParams.InterfacePinParams;
21 import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters;
22 import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerComponentParams;
23 import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams;
24 import net.mograsim.logic.model.serializing.SubmodelComponentParams.SubmodelParameters.InnerWireParams.InnerPinParams;
25 import net.mograsim.logic.model.snippets.SubmodelComponentSnippetSuppliers;
26 import net.mograsim.logic.model.snippets.symbolrenderers.SimpleRectangularLikeSymbolRenderer.SimpleRectangularLikeParams;
27 import net.mograsim.logic.model.util.JsonHandler;
28
29 /**
30  * Creates {@link SubmodelComponent}s from {@link SubmodelComponentParams}
31  * 
32  * @author Fabian Stemmler
33  * @author Daniel Kirschten
34  */
35 public final class SubmodelComponentSerializer
36 {
37         // convenience methods
38
39         /**
40          * Like {@link #deserialize(ViewModelModifiable, SubmodelComponentParams)}, but first reading the {@link SubmodelComponentParams} from
41          * the given file path.
42          * 
43          * @author Daniel Kirschten
44          */
45         public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath) throws IOException
46         {
47                 return deserialize(model, JsonHandler.readJson(sourcePath, SubmodelComponentParams.class));
48         }
49
50         /**
51          * Like {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, JsonElement)}, but first reading the
52          * {@link SubmodelComponentParams} from the given file path.
53          * 
54          * @author Daniel Kirschten
55          */
56         public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String idForSerializingOverride,
57                         JsonElement paramsForSerializingOverride) throws IOException
58         {
59                 return deserialize(model, JsonHandler.readJson(sourcePath, SubmodelComponentParams.class), idForSerializingOverride,
60                                 paramsForSerializingOverride);
61         }
62
63         /**
64          * Like {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String)}, but first reading the
65          * {@link SubmodelComponentParams} from the given file path.
66          * 
67          * @author Daniel Kirschten
68          */
69         public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String name) throws IOException
70         {
71                 return deserialize(model, JsonHandler.readJson(sourcePath, SubmodelComponentParams.class), name);
72         }
73
74         /**
75          * Like {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement)}, but first reading the
76          * {@link SubmodelComponentParams} from the given file path.
77          * 
78          * @author Daniel Kirschten
79          */
80         public static SubmodelComponent deserialize(ViewModelModifiable model, String sourcePath, String name, String idForSerializingOverride,
81                         JsonElement paramsForSerializingOverride) throws IOException
82         {
83                 return deserialize(model, JsonHandler.readJson(sourcePath, SubmodelComponentParams.class), name, idForSerializingOverride,
84                                 paramsForSerializingOverride);
85         }
86
87         /**
88          * {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement)} with no
89          * <code>idForSerializingOverride</code> set and using the default name.
90          * 
91          * @author Daniel Kirschten
92          */
93         public static SubmodelComponent deserialize(ViewModelModifiable model, SubmodelComponentParams params)
94         {
95                 return deserialize(model, params, null, null, null);
96         }
97
98         /**
99          * {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement)} using the default name.
100          * 
101          * @author Daniel Kirschten
102          */
103         public static SubmodelComponent deserialize(ViewModelModifiable model, SubmodelComponentParams params, String idForSerializingOverride,
104                         JsonElement paramsForSerializingOverride)
105         {
106                 return deserialize(model, params, null, idForSerializingOverride, paramsForSerializingOverride);
107         }
108
109         /**
110          * {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement)} with no
111          * <code>idForSerializingOverride</code> set.
112          * 
113          * @author Daniel Kirschten
114          */
115         public static SubmodelComponent deserialize(ViewModelModifiable model, SubmodelComponentParams params, String name)
116         {
117                 return deserialize(model, params, name, null, null);
118         }
119
120         /**
121          * Like {@link #serialize(SubmodelComponent)}, but instead of returning the generated {@link SubmodelComponentParams} they are written
122          * to a file at the given path.
123          * 
124          * @author Daniel Kirschten
125          */
126         public static void serialize(SubmodelComponent comp, String targetPath) throws IOException
127         {
128                 JsonHandler.writeJson(serialize(comp), targetPath);
129         }
130
131         /**
132          * Like {@link #serialize(SubmodelComponent, Function)}, but instead of returning the generated {@link SubmodelComponentParams} they are
133          * written to a file at the given path.
134          * 
135          * @author Daniel Kirschten
136          */
137         public static void serialize(SubmodelComponent comp, Function<GUIComponent, String> getIdentifier, String targetPath) throws IOException
138         {
139                 JsonHandler.writeJson(serialize(comp, getIdentifier), targetPath);
140         }
141
142         /**
143          * {@link #serialize(SubmodelComponent, Function)} using <code>"class:"</code> concatenated with a component's complete (canonical)
144          * class name for the ID of a component.
145          * 
146          * @author Daniel Kirschten
147          */
148         public static SubmodelComponentParams serialize(SubmodelComponent comp)
149         {
150                 return serialize(comp, c -> "class:" + c.getClass().getCanonicalName());
151         }
152
153         // "core" methods
154         /**
155          * Creates a {@link SubmodelComponent} from the specified {@link SubmodelComponentParams} with the given name.
156          * <p>
157          * When serializing a <code>SubmodelComponent</code>, it is undesired for every subcomponent to be serialized with its complete inner
158          * structure. Instead, these sub-<code>SubmodelComponent</code>s should be serialized with the ID and params which were used to
159          * determine the <code>SubmodelComponentParams</code> defining the sub-<code>SubmodelComponent</code>. Because of this, it is possible
160          * to override the ID and params used in {@link #serialize(SubmodelComponent, Function) serialize(...)} to describe this subcomponent.
161          * See there for details.
162          * 
163          * @author Fabian Stemmler
164          * @author Daniel Kirschten
165          */
166         @SuppressWarnings("unused") // for GUIWire being created
167         public static SubmodelComponent deserialize(ViewModelModifiable model, SubmodelComponentParams params, String name,
168                         String idForSerializingOverride, JsonElement paramsForSerializingOverride)
169         {
170                 DeserializedSubmodelComponent comp = new DeserializedSubmodelComponent(model, name, idForSerializingOverride,
171                                 paramsForSerializingOverride);
172                 comp.setSubmodelScale(params.submodel.innerScale);
173                 comp.setOutlineRenderer(SubmodelComponentSnippetSuppliers.outlineRendererSupplier
174                                 .getSnippetSupplier(params.outlineRendererSnippetID).create(comp, params.outlineRendererParams));
175                 comp.setSymbolRenderer(SubmodelComponentSnippetSuppliers.symbolRendererSupplier.getSnippetSupplier(params.symbolRendererSnippetID)
176                                 .create(comp, params.symbolRendererParams));
177                 comp.setHighLevelStateHandler(SubmodelComponentSnippetSuppliers.highLevelStateHandlerSupplier
178                                 .getSnippetSupplier(params.highLevelStateHandlerSnippetID).create(comp, params.highLevelStateHandlerParams));
179                 comp.setSize(params.width, params.height);
180                 for (InterfacePinParams iPinParams : params.interfacePins)
181                         comp.addSubmodelInterface(
182                                         new MovablePin(comp, iPinParams.name, iPinParams.logicWidth, iPinParams.location.x, iPinParams.location.y));
183                 SubmodelParameters submodelParams = params.submodel;
184                 ViewModelModifiable submodelModifiable = comp.getSubmodelModifiable();
185                 Map<String, GUIComponent> componentsByName = submodelModifiable.getComponentsByName();
186                 GUIComponent[] components = new GUIComponent[submodelParams.subComps.length];
187                 for (int i = 0; i < components.length; i++)
188                 {
189                         InnerComponentParams cParams = submodelParams.subComps[i];
190                         components[i] = IndirectGUIComponentCreator.createComponent(submodelModifiable, cParams.id, cParams.params, cParams.name);
191                         components[i].moveTo(cParams.pos.x, cParams.pos.y);
192                 }
193
194                 for (int i = 0; i < submodelParams.innerWires.length; i++)
195                 {
196                         InnerWireParams innerWire = submodelParams.innerWires[i];
197                         new GUIWire(submodelModifiable, componentsByName.get(innerWire.pin1.compName).getPin(innerWire.pin1.pinName),
198                                         componentsByName.get(innerWire.pin2.compName).getPin(innerWire.pin2.pinName), innerWire.path);
199                 }
200                 return comp;
201         }
202
203         /**
204          * Returns {@link SubmodelComponentParams}, which describe this {@link SubmodelComponent}. <br>
205          * Subcomponents are serialized in the following way: <br>
206          * If a subcomponent is a <code>SubmodelComponent</code> which has been deserialized, and it has an
207          * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set (e.g. non-null; see
208          * {@link #deserialize(ViewModelModifiable, SubmodelComponentParams, String, String, JsonElement) deserialize(...)}), this ID and the
209          * component's {@link DeserializedSubmodelComponent#paramsForSerializingOverride paramsForSerializingOverride} are written.<br>
210          * If this case doesn't apply (e.g. if the subcomponent is not a <code>SubmodelComponent</code>; or it is a
211          * <code>SubmodelComponent</code>, but hasn't been deserialized; or it has no
212          * {@link DeserializedSubmodelComponent#idForSerializingOverride idForSerializingOverride} set), the ID returned by
213          * <code>getIdentifier</code> and the params obtained by {@link GUIComponent#getParamsForSerializing() getParams()} are written.
214          * 
215          * @author Fabian Stemmler
216          * @author Daniel Kirschten
217          */
218         public static SubmodelComponentParams serialize(SubmodelComponent comp, Function<GUIComponent, String> getIdentifier)
219         {
220                 SubmodelParameters submodelParams = new SubmodelParameters();
221                 submodelParams.innerScale = comp.getSubmodelScale();
222
223                 Map<String, GUIComponent> components = new HashMap<>(comp.submodel.getComponentsByName());
224                 components.remove(SubmodelComponent.SUBMODEL_INTERFACE_NAME);
225                 InnerComponentParams[] comps = new InnerComponentParams[components.size()];
226                 int i1 = 0;
227                 for (GUIComponent innerComp : components.values())
228                 {
229                         InnerComponentParams innerParams = new InnerComponentParams();
230                         comps[i1] = innerParams;
231                         innerParams.pos = new Point(innerComp.getPosX(), innerComp.getPosY());
232                         DeserializedSubmodelComponent innerCompCasted;
233                         if (innerComp instanceof DeserializedSubmodelComponent
234                                         && (innerCompCasted = (DeserializedSubmodelComponent) innerComp).idForSerializingOverride != null)
235                         {
236                                 innerParams.id = innerCompCasted.idForSerializingOverride;
237                                 innerParams.params = innerCompCasted.paramsForSerializingOverride;
238                         } else
239                         {
240                                 innerParams.id = getIdentifier.apply(innerComp);
241                                 innerParams.params = innerComp.getParamsForSerializing();
242                         }
243                         innerParams.name = innerComp.name;
244                         i1++;
245                 }
246                 submodelParams.subComps = comps;
247
248                 List<GUIWire> wireList = comp.submodel.getWires();
249                 InnerWireParams wires[] = new InnerWireParams[wireList.size()];
250                 i1 = 0;
251                 for (GUIWire wire : wireList)
252                 {
253                         InnerWireParams inner = new InnerWireParams();
254                         wires[i1] = inner;
255                         InnerPinParams pin1Params = new InnerPinParams(), pin2Params = new InnerPinParams();
256
257                         pin1Params.pinName = wire.getPin1().name;
258                         pin1Params.compName = wire.getPin1().component.name;
259                         pin2Params.pinName = wire.getPin2().name;
260                         pin2Params.compName = wire.getPin2().component.name;
261                         inner.pin1 = pin1Params;
262                         inner.pin2 = pin2Params;
263                         inner.path = wire.getPath();
264                         i1++;
265                 }
266                 submodelParams.innerWires = wires;
267
268                 SubmodelComponentParams params = new SubmodelComponentParams();
269                 params.submodel = submodelParams;
270
271                 params.width = comp.getWidth();
272                 params.height = comp.getHeight();
273
274                 InterfacePinParams[] iPins = new InterfacePinParams[comp.getPins().size()];
275                 int i = 0;
276                 for (Pin p : comp.getPins().values())
277                 {
278                         InterfacePinParams iPinParams = new InterfacePinParams();
279                         iPins[i] = iPinParams;
280                         iPinParams.location = p.getRelPos();
281                         iPinParams.name = p.name;
282                         iPinParams.logicWidth = p.logicWidth;
283                         i++;
284                 }
285                 params.interfacePins = iPins;
286
287                 // TODO does this code belong here?
288                 if (comp instanceof SimpleRectangularSubmodelComponent)
289                 {
290                         SimpleRectangularSubmodelComponent compCasted = (SimpleRectangularSubmodelComponent) comp;
291
292                         SimpleRectangularLikeParams symbolRendererParams = new SimpleRectangularLikeParams();
293                         symbolRendererParams.centerText = compCasted.label;
294                         symbolRendererParams.centerTextHeight = SimpleRectangularSubmodelComponent.labelFontHeight;
295                         symbolRendererParams.horizontalComponentCenter = compCasted.getWidth() / 2;
296                         symbolRendererParams.pinLabelHeight = SimpleRectangularSubmodelComponent.pinNameFontHeight;
297                         symbolRendererParams.pinLabelMargin = SimpleRectangularSubmodelComponent.pinNameMargin;
298
299                         params.symbolRendererSnippetID = "simpleRectangularLike";
300                         params.symbolRendererParams = new Gson().toJsonTree(symbolRendererParams);
301                 }
302
303                 return params;
304         }
305 }