Small performance optimisations
[Mograsim.git] / net.mograsim.logic.model / src / net / mograsim / logic / model / serializing / IndirectGUIComponentCreator.java
index 6ff8adb..18d6640 100644 (file)
@@ -6,13 +6,14 @@ import java.io.UncheckedIOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-
+import java.util.Objects;
 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;
+import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
 import net.mograsim.logic.model.snippets.CodeSnippetSupplier;
 import net.mograsim.logic.model.util.JsonHandler;
 
@@ -22,10 +23,17 @@ public class IndirectGUIComponentCreator
        private static final Map<String, String> standardComponentIDsUnmodifiable = Collections.unmodifiableMap(standardComponentIDs);
 
        private static final Map<String, ComponentSupplier> componentSuppliers = new HashMap<>();
+       private static final Map<String, ResourceLoader> resourceLoaders = new HashMap<>();
+       private static final Map<String, JsonObject> componentCache = new HashMap<>();
 
        static
        {
-               try (InputStream s = IndirectGUIComponentCreator.class.getResourceAsStream("standardComponentIDMapping.json"))
+               loadStandardComponentIDs(IndirectGUIComponentCreator.class.getResourceAsStream("standardComponentIDMapping.json"));
+       }
+
+       public static void loadStandardComponentIDs(InputStream standardComponentIdMappingStream)
+       {
+               try (InputStream s = standardComponentIdMappingStream)
                {
                        if (s == null)
                                throw new IOException("Resource not found");
@@ -51,7 +59,7 @@ public class IndirectGUIComponentCreator
 
        public static void addStandardComponentID(String standardComponentID, String associatedComponentID)
        {
-               if (!associatedComponentID.startsWith("file:") && !associatedComponentID.startsWith("class:"))
+               if (!associatedComponentID.matches("(file|class|resource):.+"))
                        throw new IllegalArgumentException("Unrecognized component ID format: " + associatedComponentID);
                standardComponentIDs.put(standardComponentID, associatedComponentID);
        }
@@ -85,6 +93,8 @@ public class IndirectGUIComponentCreator
        {
                if (id != null)
                {
+                       if (componentCache.containsKey(id))
+                               return loadComponentFromJsonObject(model, id, name, componentCache.get(id));
                        String resolvedID = resolveID(id);
                        if (resolvedID != null)
                        {
@@ -96,25 +106,65 @@ public class IndirectGUIComponentCreator
                                        if (componentSupplier != null)
                                                return componentSupplier.create(model, params, name);
                                        throw new IllegalArgumentException("Component supplier not found for ID " + id + " (resolved: " + resolvedID + ")");
-                               } else
-                               // we know id has to start with "file:" here
-                               // because standardComponentIDs only contains strings starting with "class:" or "file:"
-                               if (params != null && !JsonNull.INSTANCE.equals(params))
+                               } else if (params != null && !JsonNull.INSTANCE.equals(params))
                                        throw new IllegalArgumentException("Can't give params to a component deserialized from a JSON file");
-                               try
+                               if (resolvedID.startsWith("resource:"))
                                {
-                                       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)
+                                       String[] parts = resolvedID.split(":");
+                                       if (parts.length != 3)
+                                               throw new IllegalArgumentException("invaild resource id: " + resolvedID);
+                                       String rLoadID = parts[1];
+                                       String resID = parts[2];
+                                       try
+                                       {
+                                               ResourceLoader loader;
+                                               if (!resourceLoaders.containsKey(rLoadID))
+                                               {
+                                                       Class<?> c = Class.forName(rLoadID);
+                                                       if (ResourceLoader.class.isAssignableFrom(c))
+                                                               loader = (ResourceLoader) c.getConstructor().newInstance();
+                                                       else
+                                                               loader = (ResourceLoader) Objects.requireNonNull(c.getMethod("resourceLoader").invoke(null));
+                                                       resourceLoaders.put(rLoadID, loader);
+                                               } else
+                                               {
+                                                       loader = Objects.requireNonNull(resourceLoaders.get(parts[1]));
+                                               }
+                                               if (resID.endsWith(".json"))
+                                               {
+                                                       JsonObject jsonContents = JsonHandler.readJson(loader.loadResource(resID), JsonObject.class);
+                                                       return loadComponentFromJsonObject(model, id, name, jsonContents);
+                                               }
+                                               if (!componentSuppliers.containsKey(resID))
+                                                       loader.loadClass(resID);
+                                               ComponentSupplier componentSupplier = componentSuppliers.get(resID);
+                                               if (componentSupplier != null)
+                                                       return componentSupplier.create(model, params, name);
+                                               throw new IllegalArgumentException("Component supplier not found for ID " + id + " (class cannot initialize?)");
+                                       }
+                                       catch (IOException e)
+                                       {
+                                               throw new UncheckedIOException(e);
+                                       }
+                                       catch (ClassCastException | ReflectiveOperationException e)
+                                       {
+                                               throw new IllegalArgumentException("class not found / invaild resource loader specified:" + parts[1], e);
+                                       }
+                               } else if (resolvedID.startsWith("file:"))
+                               {
+                                       try
+                                       {
+                                               String filename = resolvedID.substring(5);
+                                               JsonObject jsonContents = JsonHandler.readJson(filename, JsonObject.class);
+                                               return loadComponentFromJsonObject(model, id, name, jsonContents);
+                                       }
+                                       catch (IOException e)
+                                       {
+                                               throw new UncheckedIOException(e);
+                                       }
+                               } else
                                {
-                                       throw new UncheckedIOException(e);
+                                       throw new IllegalArgumentException("unable to resolve/interpret id" + resolvedID);
                                }
                        }
                }
@@ -123,11 +173,37 @@ public class IndirectGUIComponentCreator
 
        public static String resolveID(String id)
        {
-               if (id.startsWith("class:") || id.startsWith("file:"))
+               if (id.matches("(file|class|resource):.+"))
                        return id;
                return standardComponentIDs.get(id);
        }
 
+       private static SubmodelComponent loadComponentFromJsonObject(ViewModelModifiable model, String id, String name, JsonObject jsonContents)
+       {
+               componentCache.putIfAbsent(id, jsonContents);
+               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);
+       }
+
+       public static void registerResourceLoader(ResourceLoader resourceLoader)
+       {
+               registerResourceLoader(resourceLoader, resourceLoader.getClass());
+       }
+
+       public static void registerResourceLoader(ResourceLoader resourceLoader, Class<?> reference)
+       {
+               resourceLoaders.put(reference.getName(), Objects.requireNonNull(resourceLoader));
+       }
+
+       public static void registerResourceLoader(ResourceLoader resourceLoader, String reference)
+       {
+               resourceLoaders.put(reference, Objects.requireNonNull(resourceLoader));
+       }
+
        private static void tryLoadComponentClass(String componentClassName)
        {
                CodeSnippetSupplier.tryInvokeStaticInitializer(componentClassName, "Error loading component class %s: %s\n");