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