]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorInput2.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / workbench / ResourceEditorInput2.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 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.ui.workbench;\r
14 \r
15 import java.lang.ref.Reference;\r
16 \r
17 import org.eclipse.core.runtime.IAdaptable;\r
18 import org.eclipse.core.runtime.PlatformObject;\r
19 import org.eclipse.jface.resource.ImageDescriptor;\r
20 import org.eclipse.ui.IMemento;\r
21 import org.eclipse.ui.IPersistableElement;\r
22 import org.simantics.Simantics;\r
23 import org.simantics.db.ReadGraph;\r
24 import org.simantics.db.RequestProcessor;\r
25 import org.simantics.db.Resource;\r
26 import org.simantics.db.Session;\r
27 import org.simantics.db.common.ResourceArray;\r
28 import org.simantics.db.common.request.ReadRequest;\r
29 import org.simantics.db.exception.AdaptionException;\r
30 import org.simantics.db.exception.DatabaseException;\r
31 import org.simantics.db.exception.ResourceNotFoundException;\r
32 import org.simantics.db.layer0.exception.MissingVariableException;\r
33 import org.simantics.db.layer0.variable.RVI;\r
34 import org.simantics.db.layer0.variable.Variable;\r
35 import org.simantics.db.layer0.variable.Variables;\r
36 import org.simantics.db.request.Read;\r
37 import org.simantics.db.service.LifecycleSupport;\r
38 import org.simantics.ui.icons.ImageDescriptorProvider;\r
39 import org.simantics.ui.workbench.editor.input.ResourceEditorInputMatchingStrategy;\r
40 import org.simantics.utils.ObjectUtils;\r
41 import org.simantics.utils.datastructures.cache.ProvisionException;\r
42 import org.simantics.utils.ui.ErrorLogger;\r
43 import org.simantics.utils.ui.workbench.StringMemento;\r
44 \r
45 /**\r
46  * This is an input class for editors that have as their input the a tuple:\r
47  * (Input Resource, Model URI, RVI).\r
48  * \r
49  * Editor extensions requiring these as input should always use\r
50  * {@link ResourceEditorInputMatchingStrategy} as their matchingStrategy.\r
51  * \r
52  * @author Tuukka Lehtonen\r
53  * @see ResourceEditorInput\r
54  * @see ResourceEditorInputMatchingStrategy\r
55  */\r
56 public class ResourceEditorInput2 extends PlatformObject implements IResourceEditorInput2, IPersistableElement {\r
57 \r
58     private final static boolean          DEBUG_EXISTS    = false;\r
59     private final static boolean          DEBUG_UPDATE    = false;\r
60 \r
61     private static final String           NO_NAME         = ResourceEditorInput.NO_NAME;\r
62 \r
63     private final String                  editorID;\r
64 \r
65     /**\r
66      * A random access ID to {@link #resource}.\r
67      */\r
68     protected String                        modelId;\r
69 \r
70     protected String                        rvi;\r
71 \r
72     /**\r
73      * A random access ID to {@link #resource}.\r
74      */\r
75     private String                        resourceId;\r
76 \r
77     private transient Reference<Resource> model;\r
78 \r
79     private transient Reference<Resource> resource;\r
80 \r
81     private transient boolean             exists;\r
82 \r
83     private transient String              name;\r
84 \r
85     private transient String              tooltip;\r
86 \r
87     private transient ImageDescriptor     imageDesc;\r
88 \r
89     /** Persistent memento for external data */\r
90     private final StringMemento           persistentStore = new StringMemento();\r
91 \r
92     ResourceEditorInput2(String editorID, String resourceId, String modelId, String rvi) {\r
93         \r
94         if (editorID == null)\r
95             throw new IllegalArgumentException("null editor id");\r
96         if (resourceId == null)\r
97             throw new IllegalArgumentException("null resource id");\r
98 \r
99         this.editorID = editorID;\r
100         this.resourceId = resourceId;\r
101         this.resource = null;\r
102         this.modelId = modelId;\r
103         this.model = null;\r
104         this.rvi = rvi;\r
105 \r
106         setNonExistant();\r
107     }\r
108     \r
109     /**\r
110      * @param editorID\r
111      * @param resourceId\r
112      * @param modelURI\r
113      * @param rvi\r
114      */\r
115     public ResourceEditorInput2(String editorID, String resourceId, String modelId, RVI rvi) {\r
116         if (editorID == null)\r
117             throw new IllegalArgumentException("null editor id");\r
118         if (resourceId == null)\r
119             throw new IllegalArgumentException("null resource id");\r
120 \r
121         this.editorID = editorID;\r
122         this.resourceId = resourceId;\r
123         this.resource = null;\r
124         this.modelId = modelId;\r
125         this.model = null;\r
126         this.rvi = rvi.toString();\r
127 \r
128         setNonExistant();\r
129     }\r
130 \r
131     @Deprecated\r
132     public ResourceEditorInput2(String editorID, Resource resource, Resource model, String rvi) {\r
133         if (editorID == null)\r
134             throw new IllegalArgumentException("null editor id");\r
135         if (resource == null)\r
136             throw new IllegalArgumentException("null resource");\r
137         if (model == null)\r
138             throw new IllegalArgumentException("null model");\r
139 \r
140         this.editorID = editorID;\r
141         this.resourceId = ResourceInputs.getRandomAccessId(resource);\r
142         this.resource = ResourceInputs.makeReference(resource);\r
143         this.modelId = ResourceInputs.getRandomAccessId(model);\r
144         this.model = ResourceInputs.makeReference(model);\r
145         this.rvi = rvi;\r
146 \r
147         setNonExistant();\r
148     }\r
149     \r
150     public ResourceEditorInput2(String editorID, Resource resource, Resource model, RVI rvi) {\r
151         if (editorID == null)\r
152             throw new IllegalArgumentException("null editor id");\r
153         if (resource == null)\r
154             throw new IllegalArgumentException("null resource");\r
155         if (model == null)\r
156             throw new IllegalArgumentException("null model");\r
157 \r
158         this.editorID = editorID;\r
159         this.resourceId = ResourceInputs.getRandomAccessId(resource);\r
160         this.resource = ResourceInputs.makeReference(resource);\r
161         this.modelId = ResourceInputs.getRandomAccessId(model);\r
162         this.model = ResourceInputs.makeReference(model);\r
163         this.rvi = rvi != null ? rvi.toString() : null;\r
164 \r
165         setNonExistant();\r
166     }\r
167     \r
168     @Override\r
169     public void init(IAdaptable adapter) throws DatabaseException {\r
170         Resource r = getResource();\r
171         if (r != null)\r
172             updateCaches(getSession(), true);\r
173     }\r
174 \r
175     @Override\r
176     public void dispose() {\r
177         //System.out.println("dispose resource editor input: " + name);\r
178         // NOTE: this has to be done since Eclipse will cache these IEditorInput\r
179         // instances within EditorHistoryItem's that are stored in an EditorHistory\r
180         // instance. They are held by strong reference which means that the session\r
181         // cannot be collected if it is not nulled here.\r
182         resource = null;\r
183         model = null;\r
184     }\r
185 \r
186     /**\r
187      * @return a graph instance if it exists and has not yet been disposed,\r
188      *         <code>null</code> otherwise\r
189      */\r
190     public Session getSession() {\r
191         Session s = Simantics.getSession();\r
192         if (s.getService(LifecycleSupport.class).isClosed())\r
193             throw new IllegalStateException("database session is closed");\r
194         return s;\r
195     }\r
196 \r
197     @Override\r
198     public boolean exists() {\r
199         return exists;\r
200     }\r
201 \r
202     @Override\r
203     public boolean exists(ReadGraph graph) throws DatabaseException {\r
204         try {\r
205             assertExists(graph);\r
206             return true;\r
207         } catch (MissingVariableException e) {\r
208         } catch (ResourceNotFoundException e) {\r
209         } catch (Nonexistant e) {\r
210         }\r
211         return false;\r
212     }\r
213 \r
214     public Resource getResource0() throws DatabaseException {\r
215         Resource r = tryGetResource();\r
216         if (r != null)\r
217             return r;\r
218 \r
219         Session s = ResourceInputs.peekSession();\r
220         if (s == null)\r
221             return null;\r
222 \r
223         r = ResourceInputs.resolveResource( s, resourceId );\r
224         this.resource = ResourceInputs.makeReference( r );\r
225         return r;\r
226     }\r
227 \r
228     public Resource getModel0() throws DatabaseException {\r
229         Resource r = tryGetModel();\r
230         if (r != null)\r
231             return r;\r
232 \r
233         Session s = ResourceInputs.peekSession();\r
234         if (s == null)\r
235             return null;\r
236 \r
237         r = ResourceInputs.resolveResource( s, modelId );\r
238         this.model = ResourceInputs.makeReference( r );\r
239         return r;\r
240     }\r
241     \r
242     @Override\r
243     public Resource getResource() {\r
244         try {\r
245             return getResource0();\r
246         } catch (DatabaseException e) {\r
247             ErrorLogger.defaultLogError(e);\r
248             return null;\r
249         }\r
250     }\r
251 \r
252     @Override\r
253     @Deprecated\r
254     public ResourceArray getResourceArray() {\r
255         Resource r = getResource();\r
256         return r == null ? ResourceArray.EMPTY : new ResourceArray(r);\r
257     }\r
258     \r
259     public Resource getModel(ReadGraph graph) {\r
260         try {\r
261             return getModel0();\r
262         } catch (DatabaseException e) {\r
263             ErrorLogger.defaultLogError(e);\r
264             return null;\r
265         }\r
266     }\r
267     \r
268 \r
269     @Override\r
270     public String getRVI() {\r
271         return rvi;\r
272     }\r
273 \r
274     /* (non-Javadoc)\r
275      * @see org.eclipse.ui.IEditorInput#getImageDescriptor()\r
276      */\r
277     @Override\r
278     public ImageDescriptor getImageDescriptor() {\r
279         return imageDesc;\r
280     }\r
281 \r
282     /* (non-Javadoc)\r
283      * @see org.eclipse.ui.IEditorInput#getName()\r
284      */\r
285     @Override\r
286     public String getName() {\r
287         return name;\r
288     }\r
289 \r
290     /* (non-Javadoc)\r
291      * @see org.eclipse.ui.IEditorInput#getToolTipText()\r
292      */\r
293     @Override\r
294     public String getToolTipText() {\r
295         return tooltip;\r
296     }\r
297 \r
298     /* (non-Javadoc)\r
299      * @see org.eclipse.ui.IEditorInput#getPersistable()\r
300      */\r
301     @Override\r
302     public IPersistableElement getPersistable() {\r
303         // Don't allow persistability when it's not possible.\r
304         if (!isPersistable())\r
305             return null;\r
306         return this;\r
307     }\r
308 \r
309     protected boolean isPersistable() {\r
310         Session session = Simantics.peekSession();\r
311         if (session == null)\r
312             return false;\r
313         LifecycleSupport lc = session.peekService(LifecycleSupport.class);\r
314         if (lc == null)\r
315             return false;\r
316         if (lc.isClosed())\r
317             return false;\r
318         return true;\r
319     }\r
320 \r
321     /* (non-Javadoc)\r
322      * @see org.eclipse.ui.IPersistableElement#getFactoryId()\r
323      */\r
324     @Override\r
325     public String getFactoryId() {\r
326         return ResourceEditorInputFactory2.getFactoryId();\r
327     }\r
328 \r
329     /**\r
330      * Saves the state of the given resource editor input into the given memento.\r
331      *\r
332      * @param memento the storage area for element state\r
333      * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)\r
334      */\r
335     @Override\r
336     public void saveState(IMemento memento) {\r
337         IMemento child = memento.createChild(ResourceEditorInputFactory2.TAG_RESOURCE_ID);\r
338         child.putTextData(resourceId);\r
339         memento.putString(ResourceEditorInputFactory2.TAG_EDITOR_ID, editorID);\r
340         memento.putString(ResourceEditorInputFactory2.TAG_MODEL_ID, modelId);\r
341         memento.putString(ResourceEditorInputFactory2.TAG_RVI, rvi);\r
342         memento.putString(ResourceEditorInputFactory2.TAG_EXTERNAL_MEMENTO_ID, persistentStore.toString());\r
343     }\r
344 \r
345     /* (non-Javadoc)\r
346      * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)\r
347      */\r
348     @SuppressWarnings("rawtypes")\r
349     @Override\r
350     public Object getAdapter(Class adapter) {\r
351         //System.out.println("[ResourceEditorInput] getAdapter: " + adapter.getName());\r
352         return null;\r
353     }\r
354 \r
355     @Override\r
356     public int hashCode() {\r
357         final int prime = 31;\r
358         int result = 1;\r
359         result = prime * result + editorID.hashCode();\r
360         // modelURI and rvi may change => can't use either in hashcode.\r
361 //        result = prime * result + ObjectUtils.hashCode(modelURI);\r
362 //        result = prime * result + ObjectUtils.hashCode(rvi);\r
363         result = prime * result + ObjectUtils.hashCode(modelId);\r
364         result = prime * result + ObjectUtils.hashCode(resourceId);\r
365         return result;\r
366     }\r
367 \r
368     @Override\r
369     public boolean equals(Object obj) {\r
370         if (this == obj)\r
371             return true;\r
372         if (obj == null)\r
373             return false;\r
374         if (getClass() != obj.getClass())\r
375             return false;\r
376         ResourceEditorInput2 other = (ResourceEditorInput2) obj;\r
377         if (!editorID.equals(other.editorID))\r
378             return false;\r
379         if (!ObjectUtils.objectEquals(modelId, other.modelId))\r
380             return false;\r
381         if (!ObjectUtils.objectEquals(rvi, other.rvi))\r
382             return false;\r
383         if (!ObjectUtils.objectEquals(resourceId, other.resourceId))\r
384             return false;\r
385         return true;\r
386     }\r
387 \r
388     private void updateCaches(RequestProcessor processor, boolean sync) throws DatabaseException {\r
389         ReadRequest req = new ReadRequest() {\r
390             @Override\r
391             public void run(ReadGraph g) throws DatabaseException {\r
392                 update(g);\r
393             }\r
394         };\r
395         if (sync) {\r
396             processor.syncRequest(req);\r
397         } else {\r
398             processor.asyncRequest(req);\r
399         }\r
400     }\r
401 \r
402     static class Nonexistant extends DatabaseException {\r
403         private static final long serialVersionUID = -7964385375237203651L;\r
404 \r
405         @Override\r
406         public synchronized Throwable fillInStackTrace() {\r
407             return this;\r
408         }\r
409     }\r
410 \r
411     /* (non-Javadoc)\r
412      * @see org.simantics.ui.workbench.IResourceEditorInput#update(org.simantics.db.Graph)\r
413      */\r
414     @Override\r
415     public void update(ReadGraph g) throws DatabaseException {\r
416         Resource r = getResource();\r
417         if (r == null)\r
418             return;\r
419 \r
420         if (DEBUG_UPDATE)\r
421             System.out.println("update(" + this + ")");\r
422 \r
423         try {\r
424             assertExists(g);\r
425 \r
426             name = g.syncRequest(new TitleRequest(editorID, this));\r
427             if (name == null)\r
428                 name = NO_NAME;\r
429 \r
430             tooltip = g.syncRequest(new ToolTipRequest(editorID, this));\r
431             if (tooltip == null)\r
432                 tooltip = NO_NAME;\r
433 \r
434             try {\r
435                 ImageDescriptorProvider idp = g.adapt(r, ImageDescriptorProvider.class);\r
436                 imageDesc = idp.get();\r
437             } catch (AdaptionException e) {\r
438                 imageDesc = ImageDescriptor.getMissingImageDescriptor();\r
439             } catch (ProvisionException e) {\r
440                 imageDesc = ImageDescriptor.getMissingImageDescriptor();\r
441                 ErrorLogger.defaultLogError(e);\r
442             }\r
443 \r
444             if (DEBUG_UPDATE)\r
445                 System.out.println("update(" + this + ") finished");\r
446         } catch (DatabaseException e) {\r
447             if (DEBUG_UPDATE)\r
448                 e.printStackTrace();\r
449             setNonExistant();\r
450         }\r
451     }\r
452 \r
453     private void assertExists(ReadGraph g) throws DatabaseException {\r
454         if (DEBUG_EXISTS)\r
455             System.out.println("ResourceEditorInput2.assertExists(" + this + ") begins");\r
456 \r
457         // 1. Check resource existence\r
458         Resource r = getResource();\r
459         if (r == null)\r
460             throw new Nonexistant();\r
461 \r
462         exists = g.hasStatement(r);\r
463         if (!exists)\r
464             throw new Nonexistant();\r
465 \r
466         // 2. Check model existence\r
467         Resource model = getModel(g);\r
468         if (model == null)\r
469             throw new Nonexistant();\r
470 \r
471         exists = g.hasStatement(model);\r
472         if (!exists)\r
473             throw new Nonexistant();\r
474 \r
475         // 3. Validate rvi\r
476         if (DEBUG_EXISTS)\r
477                 System.out.println("validating rvi: '" + rvi + "'");\r
478 \r
479         if(rvi != null && !rvi.isEmpty()) {\r
480                 Variable context = Variables.getPossibleConfigurationContext(g, model);\r
481                 if (context == null)\r
482                         throw new Nonexistant();\r
483                 RVI rvi_ = RVI.fromResourceFormat(g, rvi);\r
484                 Variable variable = rvi_.resolvePossible(g, context);\r
485                 if (variable == null)\r
486                         throw new Nonexistant();\r
487         }\r
488 \r
489         // Touch the diagram title calculation within this existence\r
490         // checking request.\r
491         g.syncRequest(new TitleRequest(editorID, this));\r
492 \r
493         if (DEBUG_EXISTS)\r
494             System.out.println("ResourceEditorInput2.assertExists(" + this + ") finished");\r
495     }\r
496 \r
497     private void setNonExistant() {\r
498         if (DEBUG_UPDATE)\r
499             System.out.println("setNonExistant(" + this + " @ " + System.identityHashCode(this) + ")");\r
500 \r
501         exists = false;\r
502         tooltip = name = NO_NAME;\r
503         imageDesc = ImageDescriptor.getMissingImageDescriptor();\r
504     }\r
505 \r
506     public IMemento getPersistentStore() {\r
507         return persistentStore;\r
508     }\r
509 \r
510     @Override\r
511     public String toString() {\r
512         return getClass().getSimpleName() + " [name=" + getName() + ", resource=" + resource + ", model=" + model + ", rvi=" + rvi + "]";\r
513     }\r
514 \r
515     /**\r
516      * @see org.simantics.ui.workbench.IResourceEditorInput2#getVariable()\r
517      */\r
518     public Variable getVariable() throws DatabaseException {\r
519         return getSession().syncRequest(new Read<Variable>() {\r
520             @Override\r
521             public Variable perform(ReadGraph graph) throws DatabaseException {\r
522                 return getVariable(graph);\r
523             }\r
524         });\r
525     }\r
526 \r
527     /**\r
528      * @see org.simantics.ui.workbench.IResourceEditorInput2#getVariable(org.simantics.db.ReadGraph)\r
529      */\r
530     public Variable getVariable(ReadGraph graph) throws DatabaseException {\r
531         Resource model = getModel(graph);\r
532         String rvi = getRVI();\r
533         // Model + RVI\r
534         if (rvi != null) {\r
535             Variable configuration = Variables.getConfigurationContext(graph, model);\r
536             RVI rrvi = RVI.fromResourceFormat(graph, rvi);\r
537             return rrvi.resolve(graph, configuration);\r
538         }\r
539         // Absolute URI\r
540         else {\r
541             return Variables.getVariable(graph, model);\r
542         }\r
543     }\r
544 \r
545     private Resource tryGetResource() {\r
546         Reference<Resource> ref = resource;\r
547         return ref == null ? null : ref.get();\r
548     }\r
549 \r
550     private Resource tryGetModel() {\r
551         Reference<Resource> ref = model;\r
552         return ref == null ? null : ref.get();\r
553     }\r
554 \r
555 }