Needed to add special class loading, too
[Mograsim.git] / net.mograsim.logic.model / src / net / mograsim / logic / model / serializing / IndirectGUIComponentCreator.java
1 package net.mograsim.logic.model.serializing;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.UncheckedIOException;
6 import java.lang.reflect.InvocationTargetException;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.Map;
10 import java.util.Objects;
11 import java.util.function.Supplier;
12
13 import com.google.gson.JsonElement;
14 import com.google.gson.JsonNull;
15 import com.google.gson.JsonObject;
16
17 import net.mograsim.logic.model.model.ViewModelModifiable;
18 import net.mograsim.logic.model.model.components.GUIComponent;
19 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
20 import net.mograsim.logic.model.snippets.CodeSnippetSupplier;
21 import net.mograsim.logic.model.util.JsonHandler;
22
23 public class IndirectGUIComponentCreator
24 {
25         private static final Map<String, String> standardComponentIDs = new HashMap<>();
26         private static final Map<String, String> standardComponentIDsUnmodifiable = Collections.unmodifiableMap(standardComponentIDs);
27
28         private static final Map<String, ComponentSupplier> componentSuppliers = new HashMap<>();
29         private static final Map<String, ResourceLoader> resourceLoaders = new HashMap<>();
30
31         static
32         {
33                 loadStandardComponentIDs(IndirectGUIComponentCreator.class.getResourceAsStream("standardComponentIDMapping.json"));
34         }
35
36         public static void loadStandardComponentIDs(InputStream standardComponentIdMappingStream)
37         {
38                 try (InputStream s = standardComponentIdMappingStream)
39                 {
40                         if (s == null)
41                                 throw new IOException("Resource not found");
42                         Map<String, String> tmp = JsonHandler.readJson(s, Map.class);
43                         // don't use putAll to apply sanity checks
44                         tmp.forEach((st, id) ->
45                         {
46                                 try
47                                 {
48                                         addStandardComponentID(st, id);
49                                 }
50                                 catch (IllegalArgumentException e)
51                                 {
52                                         System.err.println("Component ID mapping contained illegal entry: " + e.getMessage());
53                                 }
54                         });
55                 }
56                 catch (IOException e)
57                 {
58                         System.err.println("Failed to initialize standard snippet ID mapping: " + e.getMessage());
59                 }
60         }
61
62         public static void addStandardComponentID(String standardComponentID, String associatedComponentID)
63         {
64                 if (!associatedComponentID.matches("(file|class|resource):.+"))
65                         throw new IllegalArgumentException("Unrecognized component ID format: " + associatedComponentID);
66                 standardComponentIDs.put(standardComponentID, associatedComponentID);
67         }
68
69         public static Map<String, String> getStandardComponentIDs()
70         {
71                 return standardComponentIDsUnmodifiable;
72         }
73
74         public static void setComponentSupplier(String className, ComponentSupplier componentSupplier)
75         {
76                 componentSuppliers.put(className, componentSupplier);
77         }
78
79         public static GUIComponent createComponent(ViewModelModifiable model, String id)
80         {
81                 return createComponent(model, id, (String) null);
82         }
83
84         public static GUIComponent createComponent(ViewModelModifiable model, String id, String name)
85         {
86                 return createComponent(model, id, JsonNull.INSTANCE, name);
87         }
88
89         public static GUIComponent createComponent(ViewModelModifiable model, String id, JsonElement params)
90         {
91                 return createComponent(model, id, params, null);
92         }
93
94         public static GUIComponent createComponent(ViewModelModifiable model, String id, JsonElement params, String name)
95         {
96                 if (id != null)
97                 {
98                         String resolvedID = resolveID(id);
99                         if (resolvedID != null)
100                         {
101                                 if (resolvedID.startsWith("class:"))
102                                 {
103                                         String className = resolvedID.substring(6);
104                                         tryLoadComponentClass(className);
105                                         ComponentSupplier componentSupplier = componentSuppliers.get(className);
106                                         if (componentSupplier != null)
107                                                 return componentSupplier.create(model, params, name);
108                                         throw new IllegalArgumentException("Component supplier not found for ID " + id + " (resolved: " + resolvedID + ")");
109                                 } else if (params != null && !JsonNull.INSTANCE.equals(params))
110                                         throw new IllegalArgumentException("Can't give params to a component deserialized from a JSON file");
111                                 if (resolvedID.startsWith("resource:"))
112                                 {
113                                         String[] parts = resolvedID.split(":");
114                                         if (parts.length != 3)
115                                                 throw new IllegalArgumentException("invaild resource id: " + resolvedID);
116                                         String rLoadID = parts[1];
117                                         String resID = parts[2];
118                                         try
119                                         {
120                                                 ResourceLoader loader;
121                                                 if (!resourceLoaders.containsKey(rLoadID))
122                                                 {
123                                                         Class<?> c = Class.forName(rLoadID);
124                                                         if (ResourceLoader.class.isAssignableFrom(c))
125                                                                 loader = (ResourceLoader) c.getConstructor().newInstance();
126                                                         else
127                                                                 loader = (ResourceLoader) Objects.requireNonNull(c.getMethod("resourceLoader").invoke(null));
128                                                         resourceLoaders.put(rLoadID, loader);
129                                                 } else
130                                                 {
131                                                         loader = Objects.requireNonNull(resourceLoaders.get(parts[1]));
132                                                 }
133                                                 if (resID.endsWith(".json"))
134                                                 {
135                                                         JsonObject jsonContents = JsonHandler.readJson(loader.loadResource(resID), JsonObject.class);
136                                                         return loadComponentFromJsonObject(model, id, name, jsonContents);
137                                                 }
138                                                 loader.loadClass(resID);
139                                                 ComponentSupplier componentSupplier = componentSuppliers.get(resID);
140                                                 if (componentSupplier != null)
141                                                         return componentSupplier.create(model, params, name);
142                                                 throw new IllegalArgumentException("Component supplier not found for ID " + id + " (class cannot initialize?)");
143                                         }
144                                         catch (IOException e)
145                                         {
146                                                 throw new UncheckedIOException(e);
147                                         }
148                                         catch (ClassCastException | ReflectiveOperationException e)
149                                         {
150                                                 throw new IllegalArgumentException("class not found / invaild resource loader specified:" + parts[1], e);
151                                         }
152                                 } else if (resolvedID.startsWith("file:"))
153                                 {
154                                         try
155                                         {
156                                                 String filename = resolvedID.substring(5);
157                                                 JsonObject jsonContents = JsonHandler.readJson(filename, JsonObject.class);
158                                                 return loadComponentFromJsonObject(model, id, name, jsonContents);
159                                         }
160                                         catch (IOException e)
161                                         {
162                                                 throw new UncheckedIOException(e);
163                                         }
164                                 } else
165                                 {
166                                         throw new IllegalArgumentException("unable to resolve/interpret id" + resolvedID);
167                                 }
168                         }
169                 }
170                 throw new RuntimeException("Could not get component supplier for ID " + id);
171         }
172
173         public static String resolveID(String id)
174         {
175                 if (id.matches("(file|class|resource):.+"))
176                         return id;
177                 return standardComponentIDs.get(id);
178         }
179
180         private static SubmodelComponent loadComponentFromJsonObject(ViewModelModifiable model, String id, String name, JsonObject jsonContents)
181         {
182                 SerializablePojo jsonContentsAsSerializablePojo = JsonHandler.parser.fromJson(jsonContents, SerializablePojo.class);
183                 if (jsonContentsAsSerializablePojo.version == null)
184                         return LegacySubmodelComponentSerializer.deserialize(model,
185                                         JsonHandler.parser.fromJson(jsonContents, LegacySubmodelComponentParams.class), name, id, null);
186                 return SubmodelComponentSerializer.deserialize(model, JsonHandler.parser.fromJson(jsonContents, SubmodelComponentParams.class),
187                                 name, id, null);
188         }
189
190         public static void registerResourceLoader(ResourceLoader resourceLoader)
191         {
192                 registerResourceLoader(resourceLoader, resourceLoader.getClass());
193         }
194
195         public static void registerResourceLoader(ResourceLoader resourceLoader, Class<?> reference)
196         {
197                 resourceLoaders.put(reference.getName(), Objects.requireNonNull(resourceLoader));
198         }
199
200         public static void registerResourceLoader(ResourceLoader resourceLoader, String reference)
201         {
202                 resourceLoaders.put(reference, Objects.requireNonNull(resourceLoader));
203         }
204
205         private static void tryLoadComponentClass(String componentClassName)
206         {
207                 CodeSnippetSupplier.tryInvokeStaticInitializer(componentClassName, "Error loading component class %s: %s\n");
208         }
209
210         public static interface ComponentSupplier
211         {
212                 public GUIComponent create(ViewModelModifiable model, JsonElement params, String name);
213         }
214 }