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