]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/runtime/RuntimeDiagramManager.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / runtime / RuntimeDiagramManager.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/runtime/RuntimeDiagramManager.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/runtime/RuntimeDiagramManager.java
new file mode 100644 (file)
index 0000000..ef117ee
--- /dev/null
@@ -0,0 +1,373 @@
+/*******************************************************************************\r
+ * Copyright (c) 2010, 2013 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *     Semantum Oy - issue #4384\r
+ *******************************************************************************/\r
+package org.simantics.diagram.runtime;\r
+\r
+import java.util.concurrent.atomic.AtomicReference;\r
+\r
+import org.eclipse.ui.IEditorInput;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.AsyncReadGraph;\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.VirtualGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.procedure.adapter.ListenerSupport;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.request.WriteResultRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.db.layer0.variable.RVI;\r
+import org.simantics.db.procedure.AsyncListener;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.operation.Layer0X;\r
+import org.simantics.ui.workbench.IResourceEditorInput2;\r
+import org.simantics.utils.datastructures.Callback;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * A helper class for managing the life-cycle of a diagram runtime realization.\r
+ * \r
+ * <p>\r
+ * The diagram runtime resource is a virtual (transient) graph database resource\r
+ * that specifies the active runtime properties of the diagram as follows:\r
+ * \r
+ * <pre>\r
+ *    RUNTIME RESOURCE\r
+ *    |---[DIAGRAM.RuntimeDiagram.HasConfiguration]---> :DIAGRAM.Diagram (directed link, no inverse relation)\r
+ *    |---[DIAGRAM.RuntimeDiagram.HasRuntimeProfile]---> :DIAGRAM.Profile (directed link, no inverse relation)\r
+ *    |---[DIAGRAM.HasModelURI]------------ "Model URI" : L0.String\r
+ *    |---[DIAGRAM.HasVariable]------------ "Variable URI" : L0.String\r
+ *    `---[DIAGRAM.HasRVI]----------------- "RVI" : L0.String\r
+ * </pre>\r
+ * \r
+ * The runtime resource itself is not attached anywhere in the graph, it only\r
+ * contains a directed link back to its diagram.\r
+ * \r
+ * <p>\r
+ * For example diagram profiles require the runtime diagram to have the\r
+ * DIAGRAM.HasVariable property. This in turn requires that the model has a\r
+ * proper {@link Layer0X#HasBaseRealization} relation in order to be resolved\r
+ * (see {@link RuntimeVariable}).\r
+ * \r
+ * <p>\r
+ * To get started using this class, see\r
+ * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} and\r
+ * {@link #create(Session, Resource, String, String)}.\r
+ * \r
+ * <p>\r
+ * Instances of this class are not thread-safe.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class RuntimeDiagramManager {\r
+\r
+    private boolean                   disposed;\r
+\r
+    private Session                   session;\r
+    private IEditorInput              editorInput;\r
+    private ListenerSupport           support;\r
+\r
+    private AtomicReference<Resource> runtimeDiagram = new AtomicReference<Resource>();\r
+\r
+    /**\r
+     * Constructs a new RuntimeDiagramManager, creates a new runtime diagram\r
+     * based on the specified editor input if it is an\r
+     * {@link IResourceEditorInput2} and starts tracking (listening) to changes\r
+     * in editor input which specifies the ModelURI and RVI needed for the\r
+     * runtime diagram. The tracking aspect comes to play in that editor inputs\r
+     * are basically allowed to change and therefore this manager tries to\r
+     * listen to changes in both the Model URI and RVI. If the input changes,\r
+     * the manager will automatically update the properties of the runtime\r
+     * diagram to match the input.\r
+     * \r
+     * <p>\r
+     * Invoking this method is equal to calling:\r
+     * \r
+     * <pre>\r
+     * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
+     * manager.track(diagram, editorInput, listenerSupport);\r
+     * </pre>\r
+     * \r
+     * @param session\r
+     * @param diagram\r
+     * @param editorInput\r
+     * @param listenerSupport\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public static RuntimeDiagramManager track(\r
+            Session session,\r
+            final Resource diagram,\r
+            IEditorInput editorInput,\r
+            ListenerSupport listenerSupport)\r
+    throws DatabaseException\r
+    {\r
+        RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
+        manager.track(diagram, editorInput, listenerSupport);\r
+        return manager;\r
+    }\r
+\r
+    /**\r
+     * Just as {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
+     * this method will construct a RuntimeDiagramManager and create a runtime\r
+     * diagram resource, but contrary to\r
+     * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} it will\r
+     * not listen to changes nor update the runtime diagram properties.\r
+     * \r
+     * <p>\r
+     * Invoking this method is equal to calling:\r
+     * <pre>\r
+     * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
+     * manager.createRuntimeDiagram(diagram, modelURI, RVI);\r
+     * </pre>\r
+     * \r
+     * @param session\r
+     * @param diagram\r
+     * @param modelURI\r
+     * @param RVI\r
+     * @return\r
+     * @throws DatabaseException\r
+     * \r
+     * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
+     */\r
+    public static RuntimeDiagramManager create(\r
+            Session session,\r
+            Resource diagram,\r
+            String modelURI,\r
+            String RVI)\r
+    throws DatabaseException\r
+    {\r
+        RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
+        manager.createRuntimeDiagram(diagram, modelURI, RVI);\r
+        return manager;\r
+    }\r
+\r
+    public RuntimeDiagramManager(Session session) {\r
+        this.session = session;\r
+    }\r
+\r
+    public Resource getRuntimeDiagram() {\r
+        return runtimeDiagram.get();\r
+    }\r
+\r
+    public void dispose() {\r
+        synchronized (this) {\r
+            assertNotDisposed();\r
+            disposed = true;\r
+        }\r
+\r
+        destroy();\r
+\r
+        this.session = null;\r
+        this.editorInput = null;\r
+        this.support = null;\r
+    }\r
+\r
+    private void assertNotDisposed() {\r
+        if (disposed)\r
+            throw new IllegalStateException(this + " is disposed");\r
+    }\r
+\r
+    private static IResourceEditorInput2 getResourceInput(Object input) {\r
+        if (input instanceof IResourceEditorInput2)\r
+            return (IResourceEditorInput2) input;\r
+        return null;\r
+    }\r
+\r
+    private IResourceEditorInput2 getResourceInput() {\r
+        return getResourceInput(editorInput);\r
+    }\r
+\r
+    /**\r
+     * Does nothing if already tracking a diagram runtime resource.\r
+     * \r
+     * @param diagram\r
+     * @param editorInput\r
+     * @param listenerSupport\r
+     * @return the current tracked diagram runtime resource\r
+     * @throws DatabaseException\r
+     * \r
+     * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
+     */\r
+    public Resource track(final Resource diagram, IEditorInput editorInput, ListenerSupport listenerSupport) throws DatabaseException {\r
+        Resource runtime = runtimeDiagram.get();\r
+        if (runtime != null)\r
+            return runtime;\r
+\r
+        if (editorInput == null)\r
+            throw new NullPointerException("null editorInput");\r
+        if (listenerSupport == null)\r
+            throw new NullPointerException("null listenerSupport");\r
+\r
+        final IResourceEditorInput2 input = getResourceInput(editorInput);\r
+        if (input == null)\r
+            return null;\r
+\r
+        this.editorInput = editorInput;\r
+        this.support = listenerSupport;\r
+\r
+        runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {\r
+            @Override\r
+            public Resource perform(WriteGraph graph) throws DatabaseException {\r
+                RuntimeDiagramDesc variable = graph.syncRequest(new RuntimeVariableForInput(input));\r
+                if (variable == null)\r
+                    return null;\r
+\r
+                final Resource runtime = createRuntimeDiagram(graph, diagram, variable);\r
+                listenRequest(graph, diagram);\r
+                return runtime;\r
+            }\r
+        });\r
+\r
+        runtimeDiagram.set(runtime);\r
+        return runtime;\r
+    }\r
+\r
+    /**\r
+     * @param diagram\r
+     * @param modelURI\r
+     * @param RVI\r
+     * @return\r
+     * @throws DatabaseException\r
+     * \r
+     * @see {@link #create(Session, Resource, String, String)\r
+     */\r
+    public Resource createRuntimeDiagram(final Resource diagram, final String modelURI, final String RVI) throws DatabaseException {\r
+        Resource runtime = runtimeDiagram.get();\r
+        if (runtime != null)\r
+            return runtime;\r
+\r
+        runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {\r
+            @Override\r
+            public Resource perform(WriteGraph graph) throws DatabaseException {\r
+                Resource model = graph.getPossibleResource(modelURI);\r
+                return createRuntimeDiagram(graph, diagram, model, RVI);\r
+            }\r
+        });\r
+\r
+        runtimeDiagram.set(runtime);\r
+        return runtime;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param diagram\r
+     * @param modelURI\r
+     * @param RVI\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource createRuntimeDiagram(WriteGraph graph, final Resource diagram, final Resource model, final String rvis) throws DatabaseException {\r
+        RVI rvi = rvis != null ? RVI.fromResourceFormat(graph, rvis) : null;\r
+        RuntimeDiagramDesc desc = graph.syncRequest(new RuntimeVariable(model, rvi, diagram));\r
+        if (desc == null)\r
+            return null;\r
+        return createRuntimeDiagram(graph, diagram, desc);\r
+    }\r
+\r
+    private void listenRequest(RequestProcessor processor, final Resource diagram) {\r
+        processor.asyncRequest(new RuntimeVariableForInput(getResourceInput()), new AsyncListener<RuntimeDiagramDesc>() {\r
+\r
+            @Override\r
+            public void exception(AsyncReadGraph graph, Throwable throwable) {\r
+                ListenerSupport s = support;\r
+                if (s != null)\r
+                    s.exception(throwable);\r
+            }\r
+\r
+            @Override\r
+            public void execute(AsyncReadGraph graph, final RuntimeDiagramDesc desc) {\r
+                if (desc == null)\r
+                    return;\r
+\r
+                Session session = graph.getSession();\r
+                session.asyncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {\r
+                    @Override\r
+                    public void perform(WriteGraph graph) throws DatabaseException {\r
+                        Resource runtime = getRuntimeDiagram();\r
+                        if (runtime != null)\r
+                            writeConfig(graph, runtime, diagram, desc);\r
+                    }\r
+                }, new Callback<DatabaseException>() {\r
+                    @Override\r
+                    public void run(DatabaseException e) {\r
+                        ListenerSupport s = support;\r
+                        if (e != null && s != null)\r
+                            s.exception(e);\r
+                    }\r
+                });\r
+            }\r
+\r
+            @Override\r
+            public boolean isDisposed() {\r
+               if(disposed) return true;\r
+               else return support != null && support.isDisposed();\r
+            }\r
+        });\r
+    }\r
+\r
+    private Resource createRuntimeDiagram(WriteGraph graph, Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {\r
+\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        final DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+        // Create the new runtime diagram instance!\r
+        final Resource runtime = graph.newResource();\r
+        graph.claim(runtime, L0.InstanceOf, null, DIA.RuntimeDiagram);\r
+        graph.claim(runtime, DIA.RuntimeDiagram_HasConfiguration, null, diagram);\r
+\r
+        writeConfig(graph, runtime, diagram, desc);\r
+\r
+        return runtime;\r
+    }\r
+\r
+    private void writeConfig(WriteGraph graph, final Resource runtime, final Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {\r
+        final DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        if (desc.getVariableURI() != null)\r
+            graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasVariable, desc.getVariableURI(), Bindings.STRING);\r
+        if (desc.getRVI() != null)\r
+            graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasRVI, desc.getRVI(), Bindings.STRING);\r
+        if (desc.getModelURI() != null)\r
+            graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasModelURI, desc.getModelURI(), Bindings.STRING);\r
+        if (desc.getActiveProfileURI() != null) {\r
+            Resource activeProfile = graph.getPossibleResource(desc.getActiveProfileURI());\r
+            if (activeProfile != null) {\r
+                graph.deny(runtime, DIA.RuntimeDiagram_HasRuntimeProfile);\r
+                graph.claim(runtime, DIA.RuntimeDiagram_HasRuntimeProfile, null, activeProfile);\r
+            }\r
+        }\r
+    }\r
+\r
+    private void destroy() {\r
+        final Resource rd = runtimeDiagram.getAndSet(null);\r
+        if (rd != null) {\r
+            try {\r
+                session.syncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {\r
+                    @Override\r
+                    public void perform(WriteGraph graph) throws DatabaseException {\r
+                        RemoverUtil.remove(graph, rd);\r
+                    }\r
+                });\r
+            } catch (DatabaseException e) {\r
+                ListenerSupport s = support;\r
+                if (s != null)\r
+                    s.exception(e);\r
+                else\r
+                    ErrorLogger.defaultLogError(e);\r
+            }\r
+        }\r
+    }\r
+\r
+}\r