A proposal for resolving the loading problem of json files
authorChristian Femers <femers@in.tum.de>
Tue, 3 Sep 2019 04:28:01 +0000 (06:28 +0200)
committerChristian Femers <femers@in.tum.de>
Tue, 3 Sep 2019 04:28:01 +0000 (06:28 +0200)
net.mograsim.logic.model.am2900/META-INF/MANIFEST.MF
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/Am2900Loader.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/standardComponentIDMapping.json [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 [new file with mode: 0644]
net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/standardComponentIDMapping.json

index fb03a1f..6dfa183 100644 (file)
@@ -3,11 +3,15 @@ Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: net.mograsim.logic.model.am2900;singleton:=true
 Bundle-Version: 0.1.0.qualifier
-Export-Package: net.mograsim.logic.model.am2900.components,
+Export-Package: net.mograsim.logic.model.am2900,
+ net.mograsim.logic.model.am2900.components,
  net.mograsim.logic.model.am2900.components.am2904,
  net.mograsim.logic.model.am2900.components.am2910,
+ net.mograsim.logic.model.am2900.machine,
  net.mograsim.logic.model.examples
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Require-Bundle: net.mograsim.logic.model;bundle-version="0.1.0";visibility:=reexport
+Require-Bundle: net.mograsim.machine;bundle-version="0.1.0";visibility:=reexport,
+ org.eclipse.osgi
 Automatic-Module-Name: net.mograsim.logic.model.am2900
 Bundle-Vendor: Mograsim Team
+Bundle-Activator: net.mograsim.logic.model.am2900.Am2900Loader
diff --git a/net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/Am2900Loader.java b/net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/Am2900Loader.java
new file mode 100644 (file)
index 0000000..a214234
--- /dev/null
@@ -0,0 +1,59 @@
+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.IndirectGUIComponentCreator;
+import net.mograsim.logic.model.serializing.ResourceLoader;
+
+public class Am2900Loader implements BundleActivator
+{
+       private static AtomicBoolean activated = new AtomicBoolean(false);
+
+       @Override
+       public void start(BundleContext context) throws Exception
+       {
+               setup(); // TODO: useful?
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception
+       {
+               // nothing
+       }
+
+       public static void setup()
+       {
+               if (activated.getAndSet(true))
+                       return;
+               IndirectGUIComponentCreator.registerResourceLoader(new Am2900ResourceLoader(), "Am2900Loader");
+               IndirectGUIComponentCreator.loadStandardComponentIDs(Am2900Loader.class.getResourceAsStream("standardComponentIDMapping.json"));
+               System.out.println("SETUP DONE");
+       }
+
+       static
+       {
+               setup();
+       }
+
+       /**
+        * @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);
+               }
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/standardComponentIDMapping.json b/net.mograsim.logic.model.am2900/src/net/mograsim/logic/model/am2900/standardComponentIDMapping.json
new file mode 100644 (file)
index 0000000..b111b71
--- /dev/null
@@ -0,0 +1,54 @@
+mograsim version: 0.1.3
+{
+  "GUIAm2904RegCTInstrDecode": "class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904RegCTInstrDecode",
+  "GUIAm2904ShiftInstrDecode": "class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904ShiftInstrDecode",
+  "GUIAm2910InstrPLA": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910InstrPLA",
+  "GUIAm2910RegCntr": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910RegCntr",
+  "GUIAm2910SP": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910SP",
+  "GUIdff12": "class:net.mograsim.logic.model.am2900.components.GUIdff12",
+  "GUIdff4_finewe": "class:net.mograsim.logic.model.am2900.components.GUIdff4_finewe",
+  "GUIinc12": "class:net.mograsim.logic.model.am2900.components.GUIinc12",
+  "GUInor12": "class:net.mograsim.logic.model.am2900.components.GUInor12",
+  "GUIram5_12": "class:net.mograsim.logic.model.am2900.components.GUIram5_12",
+  "GUIsel4_12": "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"
+}
\ No newline at end of file
index 6ff8adb..f346d02 100644 (file)
@@ -3,9 +3,12 @@ package net.mograsim.logic.model.serializing;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
+import java.util.function.Supplier;
 
 import com.google.gson.JsonElement;
 import com.google.gson.JsonNull;
@@ -13,6 +16,7 @@ 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 +26,16 @@ 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<>();
 
        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 +61,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);
        }
@@ -96,25 +106,56 @@ 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]));
+                                               }
+                                               JsonObject jsonContents = JsonHandler.readJson(loader.loadResource(resID), JsonObject.class);
+                                               return loadComponentFromJsonObject(model, id, name, jsonContents);
+                                       }
+                                       catch (IOException e)
+                                       {
+                                               throw new UncheckedIOException(e);
+                                       }
+                                       catch (ClassCastException | ReflectiveOperationException e)
+                                       {
+                                               throw new IllegalArgumentException("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 +164,36 @@ 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)
+       {
+               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");
diff --git a/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ResourceLoader.java b/net.mograsim.logic.model/src/net/mograsim/logic/model/serializing/ResourceLoader.java
new file mode 100644 (file)
index 0000000..83dd745
--- /dev/null
@@ -0,0 +1,23 @@
+package net.mograsim.logic.model.serializing;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+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
+{
+       InputStream loadResource(String path) throws IOException;
+}
index ec15a5a..ded55ba 100644 (file)
@@ -2,62 +2,13 @@ mograsim version: 0.1.3
 {
   "GUIManualSwitch": "class:net.mograsim.logic.model.model.components.atomic.GUIManualSwitch",
   "GUIBitDisplay": "class:net.mograsim.logic.model.model.components.atomic.GUIBitDisplay",
-  "GUIAm2901": "file:components/am2901/GUIAm2901.json",
-  "GUIAm2901ALUFuncDecode": "file:components/am2901/GUIAm2901ALUFuncDecode.json",
-  "GUIAm2901ALUInclDecode": "file:components/am2901/GUIAm2901ALUInclDecode.json",
-  "GUIAm2901ALUInclSourceDecodeInclFunctionDecode": "file:components/am2901/GUIAm2901ALUInclSourceDecodeInclFunctionDecode.json",
-  "GUIAm2901ALUOneBit": "file:components/am2901/GUIAm2901ALUOneBit.json",
-  "GUIAm2904": "file:components/am2904/GUIAm2904.json",
-  "GUIAm2904MSR": "file:components/am2904/GUIAm2904MSR.json",
-  "GUIAm2904muSR": "file:components/am2904/GUIAm2904muSR.json",
-  "GUIAm2904RegCTInstrDecode": "class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904RegCTInstrDecode",
-  "GUIAm2904ShiftInstrDecode": "class:net.mograsim.logic.model.am2900.components.am2904.GUIAm2904ShiftInstrDecode",
-  "GUIAm2904TestLogic": "file:components/am2904/GUIAm2904TestLogic.json",
-  "GUIAm2901DestDecode": "file:components/am2901/GUIAm2901DestDecode.json",
-  "GUIAm2901SourceDecode": "file:components/am2901/GUIAm2901SourceDecode.json",
-  "GUIAm2910InstrPLA": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910InstrPLA",
-  "GUIAm2910RegCntr": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910RegCntr",
-  "GUIAm2910SP": "class:net.mograsim.logic.model.am2900.components.am2910.GUIAm2910SP",
   "GUIAndGate": "class:net.mograsim.logic.model.model.components.atomic.GUIAndGate",
   "GUIMerger": "class:net.mograsim.logic.model.model.components.atomic.GUIMerger",
   "GUINandGate": "class:net.mograsim.logic.model.model.components.atomic.GUINandGate",
   "GUIOrGate": "class:net.mograsim.logic.model.model.components.atomic.GUIOrGate",
-  "GUI_rsLatch": "file:components/GUI_rsLatch.json",
-  "GUIand": "file:components/GUIand.json",
-  "GUIand41": "file:components/GUIand41.json",
-  "GUIandor414": "file:components/GUIandor414.json",
-  "GUIdemux2": "file:components/GUIdemux2.json",
-  "GUIdff": "file:components/GUIdff.json",
-  "GUIdff12": "class:net.mograsim.logic.model.am2900.components.GUIdff12",
-  "GUIdff4": "file:components/GUIdff4.json",
-  "GUIdff4_finewe": "class:net.mograsim.logic.model.am2900.components.GUIdff4_finewe",
-  "GUIdff4_invwe": "file:components/GUIdff4_invwe.json",
-  "GUIdlatch": "file:components/GUIdlatch.json",
-  "GUIdlatch4": "file:components/GUIdlatch4.json",
-  "GUIfulladder": "file:components/GUIfulladder.json",
-  "GUIhalfadder": "file:components/GUIhalfadder.json",
-  "GUIinc12": "class:net.mograsim.logic.model.am2900.components.GUIinc12",
-  "GUImux1": "file:components/GUImux1.json",
-  "GUImux1_4": "file:components/GUImux1_4.json",
-  "GUImux2": "file:components/GUImux2.json",
-  "GUImux2_4": "file:components/GUImux2_4.json",
-  "GUImux3": "file:components/GUImux3.json",
-  "GUInand3": "file:components/GUInand3.json",
-  "GUInor12": "class:net.mograsim.logic.model.am2900.components.GUInor12",
-  "GUInot4": "file:components/GUInot4.json",
-  "GUIor4": "file:components/GUIor4.json",
-  "GUIor_4": "file:components/GUIor_4.json",
-  "GUIram2": "file:components/GUIram2.json",
-  "GUIram4": "file:components/GUIram4.json",
-  "GUIram5_12": "class:net.mograsim.logic.model.am2900.components.GUIram5_12",
-  "GUIsel1": "file:components/GUIsel1.json",
-  "GUIsel2_4": "file:components/GUIsel2_4.json",
-  "GUIsel3_4": "file:components/GUIsel3_4.json",
-  "GUIsel4_12": "class:net.mograsim.logic.model.am2900.components.GUIsel4_12",
   "GUISplitter": "class:net.mograsim.logic.model.model.components.atomic.GUISplitter",
   "GUITriStateBuffer": "class:net.mograsim.logic.model.model.components.atomic.GUITriStateBuffer",
   "GUIClock": "class:net.mograsim.logic.model.model.components.atomic.GUIClock",
-  "GUIxor": "file:components/GUIxor.json",
   "TextComponent": "class:net.mograsim.logic.model.model.components.atomic.TextComponent",
   "WireCrossPoint": "class:net.mograsim.logic.model.model.wires.WireCrossPoint"
 }
\ No newline at end of file