Reserialized components
[Mograsim.git] / plugins / net.mograsim.logic.model.am2900 / src / net / mograsim / logic / model / examples / ReserializeAndVerifyJSONs.java
1 package net.mograsim.logic.model.examples;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.nio.file.Paths;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.Comparator;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Scanner;
15 import java.util.Set;
16 import java.util.TreeMap;
17 import java.util.stream.Collectors;
18 import java.util.stream.Stream;
19
20 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
21 import net.mograsim.logic.model.am2900.Am2900Loader;
22 import net.mograsim.logic.model.model.LogicModelModifiable;
23 import net.mograsim.logic.model.model.components.ModelComponent;
24 import net.mograsim.logic.model.model.components.atomic.ModelTextComponent;
25 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
26 import net.mograsim.logic.model.model.wires.ModelWire;
27 import net.mograsim.logic.model.model.wires.ModelWireCrossPoint;
28 import net.mograsim.logic.model.model.wires.MovablePin;
29 import net.mograsim.logic.model.model.wires.Pin;
30 import net.mograsim.logic.model.model.wires.PinUsage;
31 import net.mograsim.logic.model.serializing.DeserializedSubmodelComponent;
32 import net.mograsim.logic.model.serializing.IdentifyParams;
33 import net.mograsim.logic.model.serializing.LogicModelParams.ComponentParams;
34 import net.mograsim.logic.model.serializing.LogicModelParams.WireParams;
35 import net.mograsim.logic.model.serializing.SubmodelComponentParams;
36 import net.mograsim.logic.model.serializing.SubmodelComponentSerializer;
37 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.StandardHighLevelStateHandler.StandardHighLevelStateHandlerParams;
38 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.AtomicHighLevelStateHandler.AtomicHighLevelStateHandlerParams;
39 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.DelegatingAtomicHighLevelStateHandler.DelegatingAtomicHighLevelStateHandlerParams;
40 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.WireForcingAtomicHighLevelStateHandler.WireForcingAtomicHighLevelStateHandlerParams;
41 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent.DelegatingSubcomponentHighLevelStateHandler.DelegatingSubcomponentHighLevelStateHandlerParams;
42 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent.SubcomponentHighLevelStateHandler.SubcomponentHighLevelStateHandlerParams;
43 import net.mograsim.logic.model.util.JsonHandler;
44
45 public class ReserializeAndVerifyJSONs
46 {
47         public static double GRIDSIZE = 2.5;
48         public static boolean changePinUsages = false;
49         public static boolean changeComponentNames = false;
50         public static boolean forceDefaultComponentNames = false;
51         public static boolean changeWireNames = true;
52         public static boolean forceDefaultWireNames = true;
53         public static boolean snapWCPs = true;
54         public static boolean warnNonSnappedPoints = true;
55         public static boolean warnNonVertHorizLines = true;
56         public static boolean warnRedundantWires = true;
57
58         public static void main(String[] args) throws IOException
59         {
60                 Am2900Loader.setup();
61                 try (Scanner sysin = new Scanner(System.in))
62                 {
63                         System.out.print("Directory to search for JSONs in / JSON file to reserialize >");
64                         Path root = Paths.get(sysin.nextLine());
65                         if (!Files.exists(root))
66                                 throw new IllegalArgumentException("Path doesn't exist");
67                         if (Files.isRegularFile(root))
68                                 reserializeJSON(root, sysin);
69                         else
70                         {
71                                 System.out.print("Recursive? >");
72                                 boolean recursive = Boolean.valueOf(sysin.nextLine());
73                                 try (Stream<Path> jsons = recursive ? Files.walk(root) : Files.list(root))
74                                 {
75                                         jsons.filter(Files::isRegularFile).filter(p -> p.getFileName().toString().endsWith(".json"))
76                                                         .forEach(j -> reserializeJSON(j, sysin));
77                                 }
78                         }
79                 }
80         }
81
82         public static void reserializeJSON(Path componentPath, Scanner sysin)
83         {
84                 try
85                 {
86                         SubmodelComponentParams oldComponentJSON = JsonHandler.readJson(componentPath.toString(), SubmodelComponentParams.class);
87                         DeserializedSubmodelComponent comp = (DeserializedSubmodelComponent) SubmodelComponentSerializer
88                                         .deserialize(new LogicModelModifiable(), oldComponentJSON);
89                         System.out.println("Reserializing " + componentPath);
90                         LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
91                         Map<String, String> componentNameRemapping = new HashMap<>();
92                         Map<String, String> wireNameRemapping = new HashMap<>();
93
94                         if (changePinUsages)
95                                 changePinUsages(sysin, comp);
96                         if (changeComponentNames)
97                                 changeComponentNames(sysin, submodelModifiable, componentNameRemapping);
98                         if (changeWireNames)
99                                 changeWireNames(sysin, submodelModifiable, wireNameRemapping);
100                         if (snapWCPs)
101                                 snapWCPs(submodelModifiable);
102                         if (warnNonSnappedPoints)
103                                 warnNonSnappedPoints(comp, submodelModifiable);
104                         if (warnNonVertHorizLines)
105                                 warnNonVertHorizLines(submodelModifiable);
106                         if (warnRedundantWires)
107                                 warnRedundantWires(submodelModifiable);
108
109                         SubmodelComponentParams newComponentJSON = SubmodelComponentSerializer.serialize(comp);
110
111                         if (changeComponentNames)
112                                 changeComponentNames_AfterSerialization(newComponentJSON, componentNameRemapping);
113                         if (changeWireNames)
114                                 changeWireNames_AfterSerialization(newComponentJSON, wireNameRemapping);
115                         sortAllJSONArrays(newComponentJSON);
116
117                         JsonHandler.writeJson(newComponentJSON, componentPath.toString());
118                 }
119                 catch (Exception e)
120                 {
121                         System.err.println("An error occurred visiting " + componentPath + ":");
122                         e.printStackTrace();
123                 }
124         }
125
126         private static void warnRedundantWires(LogicModelModifiable submodelModifiable)
127         {
128                 Map<Pin, Set<Pin>> connectedPinGroups = new HashMap<>();
129                 submodelModifiable.getComponentsByName().values().stream().map(ModelComponent::getPins).map(Map::values).flatMap(Collection::stream)
130                                 .forEach(p -> connectedPinGroups.put(p, new HashSet<>(Arrays.asList(p))));
131                 submodelModifiable.getWiresByName().values().forEach(w ->
132                 {
133                         Pin pin1 = w.getPin1();
134                         Pin pin2 = w.getPin2();
135                         Set<Pin> pin1Group = connectedPinGroups.get(pin1);
136                         Set<Pin> pin2Group = connectedPinGroups.get(pin2);
137                         if (pin1Group == pin2Group)
138                                 System.out.println("  Wire " + w.name + " connecting " + pin1 + " and " + pin2 + " is redundant");
139                         else
140                         {
141                                 pin1Group.addAll(pin2Group);
142                                 pin2Group.forEach(p -> connectedPinGroups.put(p, pin1Group));
143                         }
144                 });
145         }
146
147         private static void changePinUsages(Scanner sysin, DeserializedSubmodelComponent comp)
148         {
149                 comp.getSupermodelPins().entrySet().stream().sorted(Comparator.comparing(Entry::getKey)).map(Entry::getValue).forEach(pin ->
150                 {
151                         PinUsage usage = null;
152                         while (usage == null)
153                                 try
154                                 {
155                                         System.out.print("  Usage for interface pin " + pin.name + " (empty: " + pin.usage + ") >");
156                                         String usageStr = sysin.nextLine().toUpperCase();
157                                         usage = usageStr.equals("") ? pin.usage
158                                                         : usageStr.equals("I") ? PinUsage.INPUT
159                                                                         : usageStr.equals("O") ? PinUsage.OUTPUT
160                                                                                         : usageStr.equals("T") ? PinUsage.TRISTATE : PinUsage.valueOf(usageStr);
161                                 }
162                                 catch (@SuppressWarnings("unused") IllegalArgumentException e)
163                                 {
164                                         System.err.println("  Illegal usage");
165                                 }
166                         setInterfacePinUsage(comp, pin, usage);
167                 });
168         }
169
170         @SuppressWarnings("unused") // TextComponent
171         private static void changeComponentNames(Scanner sysin, LogicModelModifiable submodelModifiable,
172                         Map<String, String> componentNameRemapping)
173         {
174                 componentNameRemapping.put(SubmodelComponent.SUBMODEL_INTERFACE_NAME, SubmodelComponent.SUBMODEL_INTERFACE_NAME);
175                 LogicModelModifiable tempModel = new LogicModelModifiable();
176                 IdentifyParams iP = new IdentifyParams();
177                 submodelModifiable.getComponentsByName().entrySet().stream()
178                                 .filter(e -> !e.getKey().equals(SubmodelComponent.SUBMODEL_INTERFACE_NAME))
179                                 .sorted(Comparator.comparing(Entry::getKey, ReserializeAndVerifyJSONs::compareStringsWithIntegers)).forEach(e ->
180                                 {
181                                         String oldName = e.getKey();
182                                         ModelComponent subcomp = e.getValue();
183                                         String defaultName = tempModel.getDefaultComponentName(subcomp);
184                                         String newName = forceDefaultComponentNames ? defaultName : null;
185                                         while (newName == null)
186                                         {
187                                                 System.out.print("  New name for component " + oldName + " of type " + subcomp.getIDForSerializing(iP) + " (empty: "
188                                                                 + defaultName + ") >");
189                                                 newName = sysin.nextLine();
190                                                 if (newName.equals(""))
191                                                         newName = defaultName;
192                                                 if (tempModel.getComponentsByName().containsKey(newName))
193                                                 {
194                                                         System.err.println("  There already is a component with that name");
195                                                         newName = null;
196                                                 }
197                                         }
198                                         componentNameRemapping.put(oldName, newName);
199                                         new ModelTextComponent(tempModel, "", newName);
200                                 });
201         }
202
203         private static void changeComponentNames_AfterSerialization(SubmodelComponentParams newComponentJSON,
204                         Map<String, String> componentNameRemapping)
205         {
206                 for (ComponentParams cParams : newComponentJSON.submodel.components)
207                         cParams.name = componentNameRemapping.get(cParams.name);
208                 for (WireParams wParams : newComponentJSON.submodel.wires)
209                 {
210                         wParams.pin1.compName = componentNameRemapping.get(wParams.pin1.compName);
211                         wParams.pin2.compName = componentNameRemapping.get(wParams.pin2.compName);
212                 }
213                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
214                 {
215                         StandardHighLevelStateHandlerParams hlshParams = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
216                                         StandardHighLevelStateHandlerParams.class);
217                         for (AtomicHighLevelStateHandlerParams ahlshParams : hlshParams.atomicHighLevelStates.values())
218                                 if ("delegating".equals(ahlshParams.id))
219                                 {
220                                         DelegatingAtomicHighLevelStateHandlerParams dhlshParams = JsonHandler.fromJsonTree(ahlshParams.params,
221                                                         DelegatingAtomicHighLevelStateHandlerParams.class);
222                                         dhlshParams.delegateTarget = componentNameRemapping.get(dhlshParams.delegateTarget);
223                                         ahlshParams.params = JsonHandler.toJsonTree(dhlshParams);
224                                 }
225                         for (SubcomponentHighLevelStateHandlerParams shlshParams : hlshParams.subcomponentHighLevelStates.values())
226                                 if ("delegating".equals(shlshParams.id))
227                                 {
228                                         DelegatingSubcomponentHighLevelStateHandlerParams dhlshParams = JsonHandler.fromJsonTree(shlshParams.params,
229                                                         DelegatingSubcomponentHighLevelStateHandlerParams.class);
230                                         dhlshParams.delegateTarget = componentNameRemapping.get(dhlshParams.delegateTarget);
231                                         shlshParams.params = JsonHandler.toJsonTree(dhlshParams);
232                                 }
233                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshParams);
234                 }
235         }
236
237         @SuppressWarnings("unused") // Wire
238         private static void changeWireNames(Scanner sysin, LogicModelModifiable submodelModifiable, Map<String, String> wireNameRemapping)
239         {
240                 LogicModelModifiable tempModel = new LogicModelModifiable();
241                 Pin p = new ModelWireCrossPoint(tempModel, 1).getPin();
242                 IdentifyParams iP = new IdentifyParams();
243                 submodelModifiable.getWiresByName().entrySet().stream()
244                                 .sorted(Comparator.comparing(Entry::getKey, ReserializeAndVerifyJSONs::compareStringsWithIntegers)).forEach(e ->
245                                 {
246                                         String oldName = e.getKey();
247                                         String defaultName = tempModel.getDefaultWireName();
248                                         String newName = forceDefaultWireNames ? defaultName : null;
249                                         while (newName == null)
250                                         {
251                                                 System.out.print("  New name for wire " + oldName + " (empty: " + defaultName + ") >");
252                                                 newName = sysin.nextLine();
253                                                 if (newName.equals(""))
254                                                         newName = defaultName;
255                                                 if (tempModel.getComponentsByName().containsKey(newName))
256                                                 {
257                                                         System.err.println("  There already is a component with that name");
258                                                         newName = null;
259                                                 }
260                                         }
261                                         wireNameRemapping.put(oldName, newName);
262                                         new ModelWire(tempModel, newName, p, p);
263                                 });
264         }
265
266         private static void changeWireNames_AfterSerialization(SubmodelComponentParams newComponentJSON, Map<String, String> wireNameRemapping)
267         {
268                 for (WireParams wParams : newComponentJSON.submodel.wires)
269                         wParams.name = wireNameRemapping.get(wParams.name);
270                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
271                 {
272                         StandardHighLevelStateHandlerParams hlshParams = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
273                                         StandardHighLevelStateHandlerParams.class);
274                         for (AtomicHighLevelStateHandlerParams ahlshParams : hlshParams.atomicHighLevelStates.values())
275                                 if ("wireForcing".equals(ahlshParams.id))
276                                 {
277                                         WireForcingAtomicHighLevelStateHandlerParams whlshParams = JsonHandler.fromJsonTree(ahlshParams.params,
278                                                         WireForcingAtomicHighLevelStateHandlerParams.class);
279                                         whlshParams.wiresToForce = whlshParams.wiresToForce.stream().map(wireNameRemapping::get).collect(Collectors.toList());
280                                         whlshParams.wiresToForceInverted = whlshParams.wiresToForceInverted.stream().map(wireNameRemapping::get)
281                                                         .collect(Collectors.toList());
282                                         ahlshParams.params = JsonHandler.toJsonTree(whlshParams);
283                                 }
284                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshParams);
285                 }
286         }
287
288         private static void snapWCPs(LogicModelModifiable submodelModifiable)
289         {
290                 submodelModifiable.getComponentsByName().values().stream().filter(c -> c instanceof ModelWireCrossPoint).forEach(c ->
291                 {
292                         double x = c.getPosX();
293                         double y = c.getPosY();
294                         double newX = x % GRIDSIZE == 0 ? x - 1 : x;
295                         double newY = y % GRIDSIZE == 0 ? y - 1 : y;
296                         if (x != newX || y != newY)
297                         {
298                                 c.moveTo(newX, newY);
299                                 System.out.println("  Snapping WCP " + c.getName());
300                         }
301                 });
302         }
303
304         private static void warnNonSnappedPoints(DeserializedSubmodelComponent comp, LogicModelModifiable submodelModifiable)
305         {
306                 if (comp.getWidth() % GRIDSIZE != 0 || comp.getHeight() % GRIDSIZE != 0)
307                         System.out.println("  Size is not snapped to grid: " + comp.getWidth() + "," + comp.getHeight());
308                 submodelModifiable.getComponentsByName().values().forEach(c ->
309                 {
310                         double x = c.getPosX();
311                         double y = c.getPosY();
312                         if (c instanceof ModelWireCrossPoint)
313                         {
314                                 x++;
315                                 y++;
316                         }
317                         if (x % GRIDSIZE != 0 || y % GRIDSIZE != 0)
318                                 System.out.println("  Component " + c.getName() + " (type " + c.getIDForSerializing(new IdentifyParams())
319                                                 + ") is not snapped to grid: " + x + "," + y);
320                 });
321                 submodelModifiable.getWiresByName().values().forEach(w ->
322                 {
323                         Point[] p = w.getPath();
324                         if (p != null)
325                                 for (int i = 0; i < p.length; i++)
326                                         if (p[i].x % GRIDSIZE != 0 || p[i].y % GRIDSIZE != 0)
327                                                 System.out.println("  Wire " + w.name + " path point #" + i + " is not snapped to grid: " + p[i].x + "," + p[i].y);
328                 });
329                 comp.getPins().values().forEach(p ->
330                 {
331                         if (p.getRelX() % GRIDSIZE != 0 || p.getRelY() % GRIDSIZE != 0)
332                                 System.out.println("  Interface point " + p.name + " is not snapped to grid: " + p.getRelX() + "," + p.getRelY());
333                 });
334         }
335
336         private static void warnNonVertHorizLines(LogicModelModifiable submodelModifiable)
337         {
338                 submodelModifiable.getWiresByName().values().forEach(w ->
339                 {
340                         double[] p = w.getEffectivePath();
341                         for (int i = 1; i < p.length / 2; i++)
342                         {
343                                 double x1 = p[2 * i - 2];
344                                 double y1 = p[2 * i - 1];
345                                 double x2 = p[2 * i + 0];
346                                 double y2 = p[2 * i + 1];
347                                 if (x1 != x2 && y1 != y2)
348                                         System.out.println("  Wire " + w.name + " part #" + (i - 1) + " is neither vertical nor horizontal");
349                         }
350                 });
351         }
352
353         private static void sortAllJSONArrays(SubmodelComponentParams newComponentJSON)
354         {
355                 Comparator<String> c = ReserializeAndVerifyJSONs::compareStringsWithIntegers;
356                 Arrays.sort(newComponentJSON.interfacePins, Comparator.comparing(p -> p.name, c));
357                 Arrays.sort(newComponentJSON.submodel.components, Comparator.comparing(p -> p.name, c));
358                 Arrays.sort(newComponentJSON.submodel.wires, Comparator.comparing(p -> p.name, c));
359                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
360                 {
361                         StandardHighLevelStateHandlerParams hlshP = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
362                                         StandardHighLevelStateHandlerParams.class);
363                         TreeMap<String, AtomicHighLevelStateHandlerParams> tmp1 = new TreeMap<>(c);
364                         tmp1.putAll(hlshP.atomicHighLevelStates);
365                         hlshP.atomicHighLevelStates = tmp1;
366                         TreeMap<String, SubcomponentHighLevelStateHandlerParams> tmp2 = new TreeMap<>(c);
367                         tmp2.putAll(hlshP.subcomponentHighLevelStates);
368                         hlshP.subcomponentHighLevelStates = tmp2;
369                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshP);
370                 }
371         }
372
373         private static int compareStringsWithIntegers(String a, String b)
374         {
375                 int aLoc = 0;
376                 int bLoc = 0;
377                 for (;;)
378                 {
379                         if (aLoc == a.length())
380                         {
381                                 if (bLoc == b.length())
382                                         return 0;
383                                 return -1;
384                         }
385                         if (bLoc == b.length())
386                                 return 1;
387                         int aInt = 1, bInt = 1;// 1 so a longer number is always greater (makes a difference for leading zeroes)
388                         boolean aHasNumber = false, bHasNumber = false;
389                         char nextCharA, nextCharB;
390                         for (;;)
391                         {
392                                 nextCharA = a.charAt(aLoc++);
393                                 if (nextCharA < '0' || nextCharA > '9')
394                                         break;
395                                 aHasNumber = true;
396                                 aInt = aInt * 10 + nextCharA - '0';
397                                 if (aLoc == a.length())
398                                         break;
399                         }
400                         for (;;)
401                         {
402                                 nextCharB = b.charAt(bLoc++);
403                                 if (nextCharB < '0' || nextCharB > '9')
404                                         break;
405                                 bHasNumber = true;
406                                 bInt = bInt * 10 + nextCharB - '0';
407                                 if (bLoc == b.length())
408                                         break;
409                         }
410                         if (aHasNumber)
411                         {
412                                 if (!bHasNumber)
413                                         return -1;
414                                 int comp = Integer.compare(aInt, bInt);
415                                 if (comp != 0)
416                                         return comp;
417                         } else
418                         {
419                                 if (bHasNumber)
420                                         return 1;
421                                 int comp = Character.compare(nextCharA, nextCharB);
422                                 if (comp != 0)
423                                         return comp;
424                         }
425                 }
426         }
427
428         private static void setInterfacePinUsage(DeserializedSubmodelComponent comp, Pin interfacePin, PinUsage usage)
429         {
430                 Set<ModelWire> wiresConnectedToPin = comp.submodel.getWiresByName().values().stream()
431                                 .filter(w -> w.getPin1() == interfacePin || w.getPin2() == interfacePin).collect(Collectors.toSet());
432                 LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
433                 wiresConnectedToPin.forEach(submodelModifiable::destroyWire);
434                 comp.removeSubmodelInterface(interfacePin.name);
435                 comp.addSubmodelInterface(new MovablePin(submodelModifiable, comp, interfacePin.name, interfacePin.logicWidth, usage,
436                                 interfacePin.getRelX(), interfacePin.getRelY()));
437                 wiresConnectedToPin.forEach(w -> new ModelWire(submodelModifiable, w.getPin1(), w.getPin2()));
438         }
439 }