Small improvements in IndirectGUIComponentCreator
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Tue, 3 Sep 2019 11:51:43 +0000 (13:51 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Tue, 3 Sep 2019 11:51:43 +0000 (13:51 +0200)
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/Am2900Loader.java
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/machine/Am2900Machine.java
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/standardComponentIDMapping.json
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/examples/GUIComponentTestbench.java
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/examples/ReserializeJSONsSettingUsages.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Testbench.java
net.mograsim.logic.model.editor/src/net/mograsim/logic/model/editor/SaveLoadManager.java
net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ClassLoaderBasedResourceLoader.java [new file with mode: 0644]
net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/IndirectGUIComponentCreator.java
net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ResourceLoader.java

index d5613b9..2b483d5 100644 (file)
@@ -1,14 +1,12 @@
 package net.mograsim.logic.model.am2900;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 
+import net.mograsim.logic.model.serializing.ClassLoaderBasedResourceLoader;
 import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
-import net.mograsim.logic.model.serializing.ResourceLoader;
 
 public class Am2900Loader implements BundleActivator
 {
@@ -30,31 +28,9 @@ public class Am2900Loader implements BundleActivator
        {
                if (activated.getAndSet(true))
                        return;
-               IndirectGUIComponentCreator.registerResourceLoader(new Am2900ResourceLoader(), "Am2900Loader");
+               ClassLoaderBasedResourceLoader resourceLoader = ClassLoaderBasedResourceLoader.create(Am2900Loader.class.getClassLoader());
+               IndirectGUIComponentCreator.registerResourceLoader(resourceLoader, "Am2900Loader");
                IndirectGUIComponentCreator.loadStandardComponentIDs(Am2900Loader.class.getResourceAsStream("standardComponentIDMapping.json"));
 //             System.out.println("SETUP DONE"); // TODO: Debug
        }
-
-       /**
-        * @see ResourceLoader
-        */
-       public static ResourceLoader resourceLoader()
-       {
-               return new Am2900ResourceLoader();
-       }
-
-       static class Am2900ResourceLoader implements ResourceLoader
-       {
-               @Override
-               public InputStream loadResource(String path) throws IOException
-               {
-                       return Am2900ResourceLoader.class.getResourceAsStream(path);
-               }
-
-               @Override
-               public Class<?> loadClass(String name) throws ClassNotFoundException
-               {
-                       return Class.forName(name, true, Am2900ResourceLoader.class.getClassLoader());
-               }
-       }
 }
index 0f0dcd5..7f234a4 100644 (file)
@@ -23,7 +23,7 @@ public class Am2900Machine implements Machine
        {
                this.machineDefinition = am2900MachineDefinition;
                viewModel = new ViewModelModifiable();
-               IndirectGUIComponentCreator.createComponent(viewModel, "resource:Am2900Loader:/components/GUIAm2900.json");
+               IndirectGUIComponentCreator.createComponent(viewModel, "resloader:Am2900Loader:jsonres:components/GUIAm2900.json");
                LogicModelParameters params = new LogicModelParameters();
                params.gateProcessTime = 50;
                params.wireTravelTime = 10;
index 7bb2e06..73bc111 100644 (file)
@@ -1,54 +1,54 @@
 mograsim version: 0.1.3
 {
-  "GUIAm2904RegCTInstrDecode": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904RegCTInstrDecode",
-  "GUIAm2904ShiftInstrDecode": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904ShiftInstrDecode",
-  "GUIAm2910InstrPLA": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910InstrPLA",
-  "GUIAm2910RegCntr": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910RegCntr",
-  "GUIAm2910SP": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910SP",
-  "GUIdff12": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUIdff12",
-  "GUIdff4_finewe": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUIdff4_finewe",
-  "GUIinc12": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUIinc12",
-  "GUInor12": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUInor12",
-  "GUIram5_12": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUIram5_12",
-  "GUIsel4_12": "resource:Am2900Loader:net.mograsim.logic.model.am2900.components.GUIsel4_12",
+  "GUIAm2904RegCTInstrDecode": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904RegCTInstrDecode",
+  "GUIAm2904ShiftInstrDecode": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904ShiftInstrDecode",
+  "GUIAm2910InstrPLA": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910InstrPLA",
+  "GUIAm2910RegCntr": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910RegCntr",
+  "GUIAm2910SP": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910SP",
+  "GUIdff12": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUIdff12",
+  "GUIdff4_finewe": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUIdff4_finewe",
+  "GUIinc12": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUIinc12",
+  "GUInor12": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUInor12",
+  "GUIram5_12": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUIram5_12",
+  "GUIsel4_12": "resloader:Am2900Loader:class:net.mograsim.logic.model.am2900.components.GUIsel4_12",
   
-  "GUIAm2901": "resource:Am2900Loader:/components/am2901/GUIAm2901.json",
-  "GUIAm2901ALUFuncDecode": "resource:Am2900Loader:/components/am2901/GUIAm2901ALUFuncDecode.json",
-  "GUIAm2901ALUInclDecode": "resource:Am2900Loader:/components/am2901/GUIAm2901ALUInclDecode.json",
-  "GUIAm2901ALUInclSourceDecodeInclFunctionDecode": "resource:Am2900Loader:/components/am2901/GUIAm2901ALUInclSourceDecodeInclFunctionDecode.json",
-  "GUIAm2901ALUOneBit": "resource:Am2900Loader:/components/am2901/GUIAm2901ALUOneBit.json",
-  "GUIAm2904": "resource:Am2900Loader:/components/am2904/GUIAm2904.json",
-  "GUIAm2910": "resource:Am2900Loader:/components/am2910/GUIAm2910.json",
-  "GUIAm2904MSR": "resource:Am2900Loader:/components/am2904/GUIAm2904MSR.json",
-  "GUIAm2904muSR": "resource:Am2900Loader:/components/am2904/GUIAm2904muSR.json",
-  "GUIAm2904TestLogic": "resource:Am2900Loader:/components/am2904/GUIAm2904TestLogic.json",
-  "GUIAm2901DestDecode": "resource:Am2900Loader:/components/am2901/GUIAm2901DestDecode.json",
-  "GUIAm2901SourceDecode": "resource:Am2900Loader:/components/am2901/GUIAm2901SourceDecode.json",
-  "GUI_rsLatch": "resource:Am2900Loader:/components/GUI_rsLatch.json",
-  "GUIand": "resource:Am2900Loader:/components/GUIand.json",
-  "GUIand41": "resource:Am2900Loader:/components/GUIand41.json",
-  "GUIandor414": "resource:Am2900Loader:/components/GUIandor414.json",
-  "GUIdemux2": "resource:Am2900Loader:/components/GUIdemux2.json",
-  "GUIdff": "resource:Am2900Loader:/components/GUIdff.json",
-  "GUIdff4": "resource:Am2900Loader:/components/GUIdff4.json",
-  "GUIdff4_invwe": "resource:Am2900Loader:/components/GUIdff4_invwe.json",
-  "GUIdlatch": "resource:Am2900Loader:/components/GUIdlatch.json",
-  "GUIdlatch4": "resource:Am2900Loader:/components/GUIdlatch4.json",
-  "GUIfulladder": "resource:Am2900Loader:/components/GUIfulladder.json",
-  "GUIhalfadder": "resource:Am2900Loader:/components/GUIhalfadder.json",
-  "GUImux1": "resource:Am2900Loader:/components/GUImux1.json",
-  "GUImux1_4": "resource:Am2900Loader:/components/GUImux1_4.json",
-  "GUImux2": "resource:Am2900Loader:/components/GUImux2.json",
-  "GUImux2_4": "resource:Am2900Loader:/components/GUImux2_4.json",
-  "GUImux3": "resource:Am2900Loader:/components/GUImux3.json",
-  "GUInand3": "resource:Am2900Loader:/components/GUInand3.json",
-  "GUInot4": "resource:Am2900Loader:/components/GUInot4.json",
-  "GUIor4": "resource:Am2900Loader:/components/GUIor4.json",
-  "GUIor_4": "resource:Am2900Loader:/components/GUIor_4.json",
-  "GUIram2": "resource:Am2900Loader:/components/GUIram2.json",
-  "GUIram4": "resource:Am2900Loader:/components/GUIram4.json",
-  "GUIsel1": "resource:Am2900Loader:/components/GUIsel1.json",
-  "GUIsel2_4": "resource:Am2900Loader:/components/GUIsel2_4.json",
-  "GUIsel3_4": "resource:Am2900Loader:/components/GUIsel3_4.json",
-  "GUIxor": "resource:Am2900Loader:/components/GUIxor.json"
+  "GUIAm2901": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901.json",
+  "GUIAm2901ALUFuncDecode": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901ALUFuncDecode.json",
+  "GUIAm2901ALUInclDecode": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901ALUInclDecode.json",
+  "GUIAm2901ALUInclSourceDecodeInclFunctionDecode": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901ALUInclSourceDecodeInclFunctionDecode.json",
+  "GUIAm2901ALUOneBit": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901ALUOneBit.json",
+  "GUIAm2904": "resloader:Am2900Loader:jsonres:components/am2904/GUIAm2904.json",
+  "GUIAm2910": "resloader:Am2900Loader:jsonres:components/am2910/GUIAm2910.json",
+  "GUIAm2904MSR": "resloader:Am2900Loader:jsonres:components/am2904/GUIAm2904MSR.json",
+  "GUIAm2904muSR": "resloader:Am2900Loader:jsonres:components/am2904/GUIAm2904muSR.json",
+  "GUIAm2904TestLogic": "resloader:Am2900Loader:jsonres:components/am2904/GUIAm2904TestLogic.json",
+  "GUIAm2901DestDecode": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901DestDecode.json",
+  "GUIAm2901SourceDecode": "resloader:Am2900Loader:jsonres:components/am2901/GUIAm2901SourceDecode.json",
+  "GUI_rsLatch": "resloader:Am2900Loader:jsonres:components/GUI_rsLatch.json",
+  "GUIand": "resloader:Am2900Loader:jsonres:components/GUIand.json",
+  "GUIand41": "resloader:Am2900Loader:jsonres:components/GUIand41.json",
+  "GUIandor414": "resloader:Am2900Loader:jsonres:components/GUIandor414.json",
+  "GUIdemux2": "resloader:Am2900Loader:jsonres:components/GUIdemux2.json",
+  "GUIdff": "resloader:Am2900Loader:jsonres:components/GUIdff.json",
+  "GUIdff4": "resloader:Am2900Loader:jsonres:components/GUIdff4.json",
+  "GUIdff4_invwe": "resloader:Am2900Loader:jsonres:components/GUIdff4_invwe.json",
+  "GUIdlatch": "resloader:Am2900Loader:jsonres:components/GUIdlatch.json",
+  "GUIdlatch4": "resloader:Am2900Loader:jsonres:components/GUIdlatch4.json",
+  "GUIfulladder": "resloader:Am2900Loader:jsonres:components/GUIfulladder.json",
+  "GUIhalfadder": "resloader:Am2900Loader:jsonres:components/GUIhalfadder.json",
+  "GUImux1": "resloader:Am2900Loader:jsonres:components/GUImux1.json",
+  "GUImux1_4": "resloader:Am2900Loader:jsonres:components/GUImux1_4.json",
+  "GUImux2": "resloader:Am2900Loader:jsonres:components/GUImux2.json",
+  "GUImux2_4": "resloader:Am2900Loader:jsonres:components/GUImux2_4.json",
+  "GUImux3": "resloader:Am2900Loader:jsonres:components/GUImux3.json",
+  "GUInand3": "resloader:Am2900Loader:jsonres:components/GUInand3.json",
+  "GUInot4": "resloader:Am2900Loader:jsonres:components/GUInot4.json",
+  "GUIor4": "resloader:Am2900Loader:jsonres:components/GUIor4.json",
+  "GUIor_4": "resloader:Am2900Loader:jsonres:components/GUIor_4.json",
+  "GUIram2": "resloader:Am2900Loader:jsonres:components/GUIram2.json",
+  "GUIram4": "resloader:Am2900Loader:jsonres:components/GUIram4.json",
+  "GUIsel1": "resloader:Am2900Loader:jsonres:components/GUIsel1.json",
+  "GUIsel2_4": "resloader:Am2900Loader:jsonres:components/GUIsel2_4.json",
+  "GUIsel3_4": "resloader:Am2900Loader:jsonres:components/GUIsel3_4.json",
+  "GUIxor": "resloader:Am2900Loader:jsonres:components/GUIxor.json"
 }
\ No newline at end of file
index 25c90ae..fbf9f2f 100644 (file)
@@ -5,6 +5,7 @@ import java.util.Comparator;
 import java.util.List;
 
 import net.mograsim.logic.model.SimpleLogicUIStandalone;
+import net.mograsim.logic.model.am2900.Am2900Loader;
 import net.mograsim.logic.model.model.ViewModelModifiable;
 import net.mograsim.logic.model.model.components.GUIComponent;
 import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
@@ -24,8 +25,8 @@ public class GUIComponentTestbench
        @SuppressWarnings("unused") // for GUIWires being created
        public static void createTestbench(ViewModelModifiable model)
        {
-               GUIComponent comp = IndirectGUIComponentCreator.createComponent(model,
-                               "resource:net.mograsim.logic.model.am2900.Am2900Loader:/components/GUIAm2900.json");
+               Am2900Loader.setup();
+               GUIComponent comp = IndirectGUIComponentCreator.createComponent(model, "resloader:Am2900Loader:jsonres:components/GUIAm2900.json");
 
                List<String> inputPinNames = new ArrayList<>();
                List<String> outputPinNames = new ArrayList<>();
index 2f41153..179265b 100644 (file)
@@ -50,7 +50,7 @@ public class ReserializeJSONsSettingUsages
                try
                {
                        DeserializedSubmodelComponent comp = (DeserializedSubmodelComponent) IndirectGUIComponentCreator
-                                       .createComponent(new ViewModelModifiable(), "file:" + json.toString());
+                                       .createComponent(new ViewModelModifiable(), "jsonfile:" + json.toString());
                        System.out.println("Reserializing " + json);
                        comp.getSupermodelPins().entrySet().stream().sorted(Comparator.comparing(Entry::getKey)).map(Entry::getValue).forEach(pin ->
                        {
index 48baa27..3999966 100644 (file)
@@ -27,7 +27,7 @@ public class Am2904Testbench
        {
                // TODO replace with proper ViewModel deserialization
                DeserializedSubmodelComponent testbench = (DeserializedSubmodelComponent) IndirectGUIComponentCreator.createComponent(model,
-                               "file:GUIAm2904Testbench.json", "testbench");
+                               "jsonfile:GUIAm2904Testbench.json", "testbench");
                testbench.setSize(1000, 1000);
                testbench.setOutlineRenderer(new Renderer()
                {
index e690200..7fecf7f 100644 (file)
@@ -69,7 +69,7 @@ public class SaveLoadManager
                if (result != null)
                {
                        new Editor((DeserializedSubmodelComponent) IndirectGUIComponentCreator.createComponent(new ViewModelModifiable(),
-                                       "file:" + result));
+                                       "jsonfile:" + result));
                }
        }
 }
diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ClassLoaderBasedResourceLoader.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ClassLoaderBasedResourceLoader.java
new file mode 100644 (file)
index 0000000..d12c4b3
--- /dev/null
@@ -0,0 +1,33 @@
+package net.mograsim.logic.model.serializing;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ClassLoaderBasedResourceLoader implements ResourceLoader
+{
+       @Override
+       public InputStream loadResource(String path) throws IOException
+       {
+               return getClassLoader().getResourceAsStream(path);
+       }
+
+       @Override
+       public Class<?> loadClass(String name) throws ClassNotFoundException
+       {
+               return Class.forName(name, true, getClassLoader());// TODO duplication
+       }
+
+       public abstract ClassLoader getClassLoader();
+
+       public static ClassLoaderBasedResourceLoader create(ClassLoader loader)
+       {
+               return new ClassLoaderBasedResourceLoader()
+               {
+                       @Override
+                       public ClassLoader getClassLoader()
+                       {
+                               return loader;
+                       }
+               };
+       }
+}
\ No newline at end of file
index 18d6640..2ac2c47 100644 (file)
@@ -7,6 +7,7 @@ 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;
@@ -26,8 +27,10 @@ public class IndirectGUIComponentCreator
        private static final Map<String, ResourceLoader> resourceLoaders = new HashMap<>();
        private static final Map<String, JsonObject> componentCache = new HashMap<>();
 
+       private static final ResourceLoader defaultResourceLoader;
        static
        {
+               defaultResourceLoader = ClassLoaderBasedResourceLoader.create(IndirectGUIComponentCreator.class.getClassLoader());
                loadStandardComponentIDs(IndirectGUIComponentCreator.class.getResourceAsStream("standardComponentIDMapping.json"));
        }
 
@@ -59,7 +62,7 @@ public class IndirectGUIComponentCreator
 
        public static void addStandardComponentID(String standardComponentID, String associatedComponentID)
        {
-               if (!associatedComponentID.matches("(file|class|resource):.+"))
+               if (!checkIDIsValidResolvedID(associatedComponentID))
                        throw new IllegalArgumentException("Unrecognized component ID format: " + associatedComponentID);
                standardComponentIDs.put(standardComponentID, associatedComponentID);
        }
@@ -91,93 +94,97 @@ public class IndirectGUIComponentCreator
 
        public static GUIComponent createComponent(ViewModelModifiable model, String id, JsonElement params, String name)
        {
-               if (id != null)
+               if (id == null)
+                       throw new NullPointerException("Component ID is null");
+               if (componentCache.containsKey(id))
+                       return loadComponentFromJsonObject(model, id, name, componentCache.get(id));
+               String resolvedID = resolveID(id);
+               if (resolvedID == null)
+                       throw new IllegalArgumentException("Unknown standard ID or illegal resolved ID: " + id);
+               String[] parts = resolvedID.split(":");
+               String firstPart = parts[0];
+               if (firstPart.equals("jsonfile"))
                {
-                       if (componentCache.containsKey(id))
-                               return loadComponentFromJsonObject(model, id, name, componentCache.get(id));
-                       String resolvedID = resolveID(id);
-                       if (resolvedID != null)
+                       JsonObject jsonContents;
+                       try
                        {
-                               if (resolvedID.startsWith("class:"))
-                               {
-                                       String className = resolvedID.substring(6);
-                                       tryLoadComponentClass(className);
-                                       ComponentSupplier componentSupplier = componentSuppliers.get(className);
-                                       if (componentSupplier != null)
-                                               return componentSupplier.create(model, params, name);
-                                       throw new IllegalArgumentException("Component supplier not found for ID " + id + " (resolved: " + resolvedID + ")");
-                               } else if (params != null && !JsonNull.INSTANCE.equals(params))
-                                       throw new IllegalArgumentException("Can't give params to a component deserialized from a JSON file");
-                               if (resolvedID.startsWith("resource:"))
-                               {
-                                       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:"))
+                               // don't use parts[1], because the path could contain ':'
+                               jsonContents = JsonHandler.readJson(resolvedID.substring("jsonfile:".length()), JsonObject.class);
+                       }
+                       catch (IOException e)
+                       {
+                               throw new UncheckedIOException("Error loading JSON file", e);
+                       }
+                       return loadComponentFromJsonObject(model, id, name, jsonContents);
+               }
+               ResourceLoader loader;
+               String resTypeID;
+               String resID;
+               if (firstPart.equals("resloader"))
+               {
+                       String loaderID = parts[1];
+                       loader = resourceLoaders.get(loaderID);
+                       if (loader == null)
+                               tryLoadResourceLoader(loaderID);
+                       loader = resourceLoaders.get(loaderID);
+                       if (loader == null)
+                               throw new IllegalArgumentException(
+                                               "Unknown resource loader: " + loaderID + " (but class was found. Probably the static initializer is missing)");
+                       resTypeID = parts[2];
+                       resID = parts[3];
+               } else
+               {
+                       loader = defaultResourceLoader;
+                       resTypeID = parts[0];
+                       resID = parts[1];
+               }
+               if (resTypeID.equals("jsonres"))
+               {
+                       JsonObject jsonContents;
+                       try
+                       {
+                               @SuppressWarnings("resource") // jsonStream is closed in JsonHandler
+                               InputStream jsonStream = Objects.requireNonNull(loader.loadResource(resID), "Error loading JSON resource: Not found");
+                               jsonContents = JsonHandler.readJson(jsonStream, JsonObject.class);
+                       }
+                       catch (IOException e)
+                       {
+                               throw new UncheckedIOException("Error loading JSON resource", e);
+                       }
+                       return loadComponentFromJsonObject(model, id, name, jsonContents);
+               } else if (resTypeID.equals("class"))
+               {
+                       ComponentSupplier componentSupplier = componentSuppliers.get(resID);
+                       if (componentSupplier == null)
+                               try
                                {
-                                       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
+                                       loader.loadClass(resID);
+                               }
+                               catch (@SuppressWarnings("unused") ClassNotFoundException e)
                                {
-                                       throw new IllegalArgumentException("unable to resolve/interpret id" + resolvedID);
+                                       throw new IllegalArgumentException("Unknown component supplier: " + resID);
                                }
-                       }
-               }
-               throw new RuntimeException("Could not get component supplier for ID " + id);
+                       componentSupplier = componentSuppliers.get(resID);
+                       if (componentSupplier == null)
+                               throw new IllegalArgumentException(
+                                               "Unknown component supplier: " + resID + " (but class was found. Probably the static initializer is missing)");
+                       return componentSupplier.create(model, params, name);
+               } else
+                       throw new IllegalStateException("Unknown resource type ID: " + resTypeID);
        }
 
        public static String resolveID(String id)
        {
-               if (id.matches("(file|class|resource):.+"))
+               if (checkIDIsValidResolvedID(id))
                        return id;
                return standardComponentIDs.get(id);
        }
 
+       private static boolean checkIDIsValidResolvedID(String id)
+       {
+               return id.matches("jsonfile:(.+)|(resloader:([^:]+):)?(jsonres|class):[^:]+");
+       }
+
        private static SubmodelComponent loadComponentFromJsonObject(ViewModelModifiable model, String id, String name, JsonObject jsonContents)
        {
                componentCache.putIfAbsent(id, jsonContents);
@@ -204,9 +211,9 @@ public class IndirectGUIComponentCreator
                resourceLoaders.put(reference, Objects.requireNonNull(resourceLoader));
        }
 
-       private static void tryLoadComponentClass(String componentClassName)
+       private static void tryLoadResourceLoader(String loaderClassName)
        {
-               CodeSnippetSupplier.tryInvokeStaticInitializer(componentClassName, "Error loading component class %s: %s\n");
+               CodeSnippetSupplier.tryInvokeStaticInitializer(loaderClassName, "Error loading resoruce loader %s: %s\n");
        }
 
        public static interface ComponentSupplier
index 1052d5f..ff7fd07 100644 (file)
@@ -7,15 +7,6 @@ import net.mograsim.logic.model.model.components.GUIComponent;
 
 /**
  * For loading JSON {@link GUIComponent}s from other OSGI-Modules or jar-Files.
- * <p>
- * An implementation must conform to either of the following rules:
- * <ul>
- * <li>A ResourceLoader class can be referenced directly in <code>resource:qualifiedClassName:jsonResourcePath</code> and is then
- * instantiated using the public default constructor.</li>
- * <li>A class referenced in <code>resource:qualifiedClassName:jsonResourcePath</code> provides a public static method called
- * <b><code>resourceLoader</code></b> that returns an (non-null) object of the type {@link ResourceLoader}</li>
- * </ul>
- *
  */
 public interface ResourceLoader
 {