]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 /*******************************************************************************\r
2  * Copyright (c) 2010, 2013 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *     Semantum Oy - issue #4384\r
12  *******************************************************************************/\r
13 package org.simantics.diagram.runtime;\r
14 \r
15 import java.util.concurrent.atomic.AtomicReference;\r
16 \r
17 import org.eclipse.ui.IEditorInput;\r
18 import org.simantics.databoard.Bindings;\r
19 import org.simantics.db.AsyncReadGraph;\r
20 import org.simantics.db.RequestProcessor;\r
21 import org.simantics.db.Resource;\r
22 import org.simantics.db.Session;\r
23 import org.simantics.db.VirtualGraph;\r
24 import org.simantics.db.WriteGraph;\r
25 import org.simantics.db.common.procedure.adapter.ListenerSupport;\r
26 import org.simantics.db.common.request.WriteRequest;\r
27 import org.simantics.db.common.request.WriteResultRequest;\r
28 import org.simantics.db.exception.DatabaseException;\r
29 import org.simantics.db.layer0.util.RemoverUtil;\r
30 import org.simantics.db.layer0.variable.RVI;\r
31 import org.simantics.db.procedure.AsyncListener;\r
32 import org.simantics.diagram.stubs.DiagramResource;\r
33 import org.simantics.layer0.Layer0;\r
34 import org.simantics.operation.Layer0X;\r
35 import org.simantics.ui.workbench.IResourceEditorInput2;\r
36 import org.simantics.utils.datastructures.Callback;\r
37 import org.simantics.utils.ui.ErrorLogger;\r
38 \r
39 /**\r
40  * A helper class for managing the life-cycle of a diagram runtime realization.\r
41  * \r
42  * <p>\r
43  * The diagram runtime resource is a virtual (transient) graph database resource\r
44  * that specifies the active runtime properties of the diagram as follows:\r
45  * \r
46  * <pre>\r
47  *    RUNTIME RESOURCE\r
48  *    |---[DIAGRAM.RuntimeDiagram.HasConfiguration]---> :DIAGRAM.Diagram (directed link, no inverse relation)\r
49  *    |---[DIAGRAM.RuntimeDiagram.HasRuntimeProfile]---> :DIAGRAM.Profile (directed link, no inverse relation)\r
50  *    |---[DIAGRAM.HasModelURI]------------ "Model URI" : L0.String\r
51  *    |---[DIAGRAM.HasVariable]------------ "Variable URI" : L0.String\r
52  *    `---[DIAGRAM.HasRVI]----------------- "RVI" : L0.String\r
53  * </pre>\r
54  * \r
55  * The runtime resource itself is not attached anywhere in the graph, it only\r
56  * contains a directed link back to its diagram.\r
57  * \r
58  * <p>\r
59  * For example diagram profiles require the runtime diagram to have the\r
60  * DIAGRAM.HasVariable property. This in turn requires that the model has a\r
61  * proper {@link Layer0X#HasBaseRealization} relation in order to be resolved\r
62  * (see {@link RuntimeVariable}).\r
63  * \r
64  * <p>\r
65  * To get started using this class, see\r
66  * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} and\r
67  * {@link #create(Session, Resource, String, String)}.\r
68  * \r
69  * <p>\r
70  * Instances of this class are not thread-safe.\r
71  * \r
72  * @author Tuukka Lehtonen\r
73  */\r
74 public class RuntimeDiagramManager {\r
75 \r
76     private boolean                   disposed;\r
77 \r
78     private Session                   session;\r
79     private IEditorInput              editorInput;\r
80     private ListenerSupport           support;\r
81 \r
82     private AtomicReference<Resource> runtimeDiagram = new AtomicReference<Resource>();\r
83 \r
84     /**\r
85      * Constructs a new RuntimeDiagramManager, creates a new runtime diagram\r
86      * based on the specified editor input if it is an\r
87      * {@link IResourceEditorInput2} and starts tracking (listening) to changes\r
88      * in editor input which specifies the ModelURI and RVI needed for the\r
89      * runtime diagram. The tracking aspect comes to play in that editor inputs\r
90      * are basically allowed to change and therefore this manager tries to\r
91      * listen to changes in both the Model URI and RVI. If the input changes,\r
92      * the manager will automatically update the properties of the runtime\r
93      * diagram to match the input.\r
94      * \r
95      * <p>\r
96      * Invoking this method is equal to calling:\r
97      * \r
98      * <pre>\r
99      * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
100      * manager.track(diagram, editorInput, listenerSupport);\r
101      * </pre>\r
102      * \r
103      * @param session\r
104      * @param diagram\r
105      * @param editorInput\r
106      * @param listenerSupport\r
107      * @return\r
108      * @throws DatabaseException\r
109      */\r
110     public static RuntimeDiagramManager track(\r
111             Session session,\r
112             final Resource diagram,\r
113             IEditorInput editorInput,\r
114             ListenerSupport listenerSupport)\r
115     throws DatabaseException\r
116     {\r
117         RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
118         manager.track(diagram, editorInput, listenerSupport);\r
119         return manager;\r
120     }\r
121 \r
122     /**\r
123      * Just as {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
124      * this method will construct a RuntimeDiagramManager and create a runtime\r
125      * diagram resource, but contrary to\r
126      * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} it will\r
127      * not listen to changes nor update the runtime diagram properties.\r
128      * \r
129      * <p>\r
130      * Invoking this method is equal to calling:\r
131      * <pre>\r
132      * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
133      * manager.createRuntimeDiagram(diagram, modelURI, RVI);\r
134      * </pre>\r
135      * \r
136      * @param session\r
137      * @param diagram\r
138      * @param modelURI\r
139      * @param RVI\r
140      * @return\r
141      * @throws DatabaseException\r
142      * \r
143      * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
144      */\r
145     public static RuntimeDiagramManager create(\r
146             Session session,\r
147             Resource diagram,\r
148             String modelURI,\r
149             String RVI)\r
150     throws DatabaseException\r
151     {\r
152         RuntimeDiagramManager manager = new RuntimeDiagramManager(session);\r
153         manager.createRuntimeDiagram(diagram, modelURI, RVI);\r
154         return manager;\r
155     }\r
156 \r
157     public RuntimeDiagramManager(Session session) {\r
158         this.session = session;\r
159     }\r
160 \r
161     public Resource getRuntimeDiagram() {\r
162         return runtimeDiagram.get();\r
163     }\r
164 \r
165     public void dispose() {\r
166         synchronized (this) {\r
167             assertNotDisposed();\r
168             disposed = true;\r
169         }\r
170 \r
171         destroy();\r
172 \r
173         this.session = null;\r
174         this.editorInput = null;\r
175         this.support = null;\r
176     }\r
177 \r
178     private void assertNotDisposed() {\r
179         if (disposed)\r
180             throw new IllegalStateException(this + " is disposed");\r
181     }\r
182 \r
183     private static IResourceEditorInput2 getResourceInput(Object input) {\r
184         if (input instanceof IResourceEditorInput2)\r
185             return (IResourceEditorInput2) input;\r
186         return null;\r
187     }\r
188 \r
189     private IResourceEditorInput2 getResourceInput() {\r
190         return getResourceInput(editorInput);\r
191     }\r
192 \r
193     /**\r
194      * Does nothing if already tracking a diagram runtime resource.\r
195      * \r
196      * @param diagram\r
197      * @param editorInput\r
198      * @param listenerSupport\r
199      * @return the current tracked diagram runtime resource\r
200      * @throws DatabaseException\r
201      * \r
202      * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}\r
203      */\r
204     public Resource track(final Resource diagram, IEditorInput editorInput, ListenerSupport listenerSupport) throws DatabaseException {\r
205         Resource runtime = runtimeDiagram.get();\r
206         if (runtime != null)\r
207             return runtime;\r
208 \r
209         if (editorInput == null)\r
210             throw new NullPointerException("null editorInput");\r
211         if (listenerSupport == null)\r
212             throw new NullPointerException("null listenerSupport");\r
213 \r
214         final IResourceEditorInput2 input = getResourceInput(editorInput);\r
215         if (input == null)\r
216             return null;\r
217 \r
218         this.editorInput = editorInput;\r
219         this.support = listenerSupport;\r
220 \r
221         runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {\r
222             @Override\r
223             public Resource perform(WriteGraph graph) throws DatabaseException {\r
224                 RuntimeDiagramDesc variable = graph.syncRequest(new RuntimeVariableForInput(input));\r
225                 if (variable == null)\r
226                     return null;\r
227 \r
228                 final Resource runtime = createRuntimeDiagram(graph, diagram, variable);\r
229                 listenRequest(graph, diagram);\r
230                 return runtime;\r
231             }\r
232         });\r
233 \r
234         runtimeDiagram.set(runtime);\r
235         return runtime;\r
236     }\r
237 \r
238     /**\r
239      * @param diagram\r
240      * @param modelURI\r
241      * @param RVI\r
242      * @return\r
243      * @throws DatabaseException\r
244      * \r
245      * @see {@link #create(Session, Resource, String, String)\r
246      */\r
247     public Resource createRuntimeDiagram(final Resource diagram, final String modelURI, final String RVI) throws DatabaseException {\r
248         Resource runtime = runtimeDiagram.get();\r
249         if (runtime != null)\r
250             return runtime;\r
251 \r
252         runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {\r
253             @Override\r
254             public Resource perform(WriteGraph graph) throws DatabaseException {\r
255                 Resource model = graph.getPossibleResource(modelURI);\r
256                 return createRuntimeDiagram(graph, diagram, model, RVI);\r
257             }\r
258         });\r
259 \r
260         runtimeDiagram.set(runtime);\r
261         return runtime;\r
262     }\r
263 \r
264     /**\r
265      * @param graph\r
266      * @param diagram\r
267      * @param modelURI\r
268      * @param RVI\r
269      * @return\r
270      * @throws DatabaseException\r
271      */\r
272     public Resource createRuntimeDiagram(WriteGraph graph, final Resource diagram, final Resource model, final String rvis) throws DatabaseException {\r
273         RVI rvi = rvis != null ? RVI.fromResourceFormat(graph, rvis) : null;\r
274         RuntimeDiagramDesc desc = graph.syncRequest(new RuntimeVariable(model, rvi, diagram));\r
275         if (desc == null)\r
276             return null;\r
277         return createRuntimeDiagram(graph, diagram, desc);\r
278     }\r
279 \r
280     private void listenRequest(RequestProcessor processor, final Resource diagram) {\r
281         processor.asyncRequest(new RuntimeVariableForInput(getResourceInput()), new AsyncListener<RuntimeDiagramDesc>() {\r
282 \r
283             @Override\r
284             public void exception(AsyncReadGraph graph, Throwable throwable) {\r
285                 ListenerSupport s = support;\r
286                 if (s != null)\r
287                     s.exception(throwable);\r
288             }\r
289 \r
290             @Override\r
291             public void execute(AsyncReadGraph graph, final RuntimeDiagramDesc desc) {\r
292                 if (desc == null)\r
293                     return;\r
294 \r
295                 Session session = graph.getSession();\r
296                 session.asyncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {\r
297                     @Override\r
298                     public void perform(WriteGraph graph) throws DatabaseException {\r
299                         Resource runtime = getRuntimeDiagram();\r
300                         if (runtime != null)\r
301                             writeConfig(graph, runtime, diagram, desc);\r
302                     }\r
303                 }, new Callback<DatabaseException>() {\r
304                     @Override\r
305                     public void run(DatabaseException e) {\r
306                         ListenerSupport s = support;\r
307                         if (e != null && s != null)\r
308                             s.exception(e);\r
309                     }\r
310                 });\r
311             }\r
312 \r
313             @Override\r
314             public boolean isDisposed() {\r
315                 if(disposed) return true;\r
316                 else return support != null && support.isDisposed();\r
317             }\r
318         });\r
319     }\r
320 \r
321     private Resource createRuntimeDiagram(WriteGraph graph, Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {\r
322 \r
323         Layer0 L0 = Layer0.getInstance(graph);\r
324         final DiagramResource DIA = DiagramResource.getInstance(graph);\r
325 \r
326         // Create the new runtime diagram instance!\r
327         final Resource runtime = graph.newResource();\r
328         graph.claim(runtime, L0.InstanceOf, null, DIA.RuntimeDiagram);\r
329         graph.claim(runtime, DIA.RuntimeDiagram_HasConfiguration, null, diagram);\r
330 \r
331         writeConfig(graph, runtime, diagram, desc);\r
332 \r
333         return runtime;\r
334     }\r
335 \r
336     private void writeConfig(WriteGraph graph, final Resource runtime, final Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {\r
337         final DiagramResource DIA = DiagramResource.getInstance(graph);\r
338         if (desc.getVariableURI() != null)\r
339             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasVariable, desc.getVariableURI(), Bindings.STRING);\r
340         if (desc.getRVI() != null)\r
341             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasRVI, desc.getRVI(), Bindings.STRING);\r
342         if (desc.getModelURI() != null)\r
343             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasModelURI, desc.getModelURI(), Bindings.STRING);\r
344         if (desc.getActiveProfileURI() != null) {\r
345             Resource activeProfile = graph.getPossibleResource(desc.getActiveProfileURI());\r
346             if (activeProfile != null) {\r
347                 graph.deny(runtime, DIA.RuntimeDiagram_HasRuntimeProfile);\r
348                 graph.claim(runtime, DIA.RuntimeDiagram_HasRuntimeProfile, null, activeProfile);\r
349             }\r
350         }\r
351     }\r
352 \r
353     private void destroy() {\r
354         final Resource rd = runtimeDiagram.getAndSet(null);\r
355         if (rd != null) {\r
356             try {\r
357                 session.syncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {\r
358                     @Override\r
359                     public void perform(WriteGraph graph) throws DatabaseException {\r
360                         RemoverUtil.remove(graph, rd);\r
361                     }\r
362                 });\r
363             } catch (DatabaseException e) {\r
364                 ListenerSupport s = support;\r
365                 if (s != null)\r
366                     s.exception(e);\r
367                 else\r
368                     ErrorLogger.defaultLogError(e);\r
369             }\r
370         }\r
371     }\r
372 \r
373 }\r