]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorInput.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / workbench / ResourceEditorInput.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 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  *******************************************************************************/
12 package org.simantics.ui.workbench;
13
14 import java.lang.ref.Reference;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18
19 import org.eclipse.core.runtime.IAdaptable;
20 import org.eclipse.core.runtime.PlatformObject;
21 import org.eclipse.jface.resource.ImageDescriptor;
22 import org.eclipse.ui.IMemento;
23 import org.eclipse.ui.IPersistableElement;
24 import org.simantics.Simantics;
25 import org.simantics.db.ReadGraph;
26 import org.simantics.db.Resource;
27 import org.simantics.db.Session;
28 import org.simantics.db.common.ResourceArray;
29 import org.simantics.db.common.request.ReadRequest;
30 import org.simantics.db.exception.AdaptionException;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.service.LifecycleSupport;
33 import org.simantics.ui.icons.ImageDescriptorProvider;
34 import org.simantics.ui.workbench.editor.input.ResourceEditorInputMatchingStrategy;
35 import org.simantics.utils.ObjectUtils;
36 import org.simantics.utils.datastructures.cache.ProvisionException;
37 import org.simantics.utils.ui.ErrorLogger;
38 import org.simantics.utils.ui.workbench.StringMemento;
39
40 /**
41  * A basic editor input for Simantics database {@link Resource} instances.
42  * 
43  * Editor extensions requiring these as input should always use
44  * {@link ResourceEditorInputMatchingStrategy} as their matchingStrategy.
45  * 
46  * @author Tuukka Lehtonen
47  * 
48  * @see ResourceEditorInput2
49  * @see ResourceEditorInputMatchingStrategy
50  */
51 public class ResourceEditorInput extends PlatformObject implements IResourceEditorInput, IPersistableElement {
52
53     static final String                        NO_NAME         = "(no name)";
54
55     private final String                       editorID;
56
57     private List<String>                       resourceIds;
58
59     private transient Reference<ResourceArray> resources;
60
61     private transient boolean                  exists;
62
63     private transient String                   name;
64
65     private transient String                   tooltip;
66
67     private transient ImageDescriptor          imageDesc;
68
69     /** Persistent memento for external data */
70     private final StringMemento                persistentStore = new StringMemento();
71
72     /**
73      * @param editorID
74      * @param r
75      */
76     public ResourceEditorInput(String editorID, Resource r) {
77         this(editorID, new ResourceArray(r));
78     }
79
80     /**
81      * @param editorID
82      * @param ra
83      */
84     public ResourceEditorInput(String editorID, ResourceArray ra) {
85         if (editorID == null)
86             throw new IllegalArgumentException("null editor id");
87         if (ra == null)
88             throw new IllegalArgumentException("null resource array");
89         if (ra.isEmpty())
90             throw new IllegalArgumentException("input resource array is empty, expected non-empty list");
91         for (Resource r : ra.resources)
92             if (r == null)
93                 throw new IllegalArgumentException("input resource array contains null resources: " + ra);
94
95         this.editorID = editorID;
96         this.resources = ResourceInputs.makeReference(ra);
97         this.resourceIds = ResourceInputs.getRandomAccessIds(ra);
98
99         setNonExistant();
100     }
101
102     /**
103      * @param editorID
104      * @param randomAccessResourceId
105      */
106     public ResourceEditorInput(String editorID, String randomAccessResourceId) {
107         this(editorID, Collections.singletonList(randomAccessResourceId));
108     }
109
110     /**
111      * @param editorID
112      * @param randomAccessResourceId a non-empty list of random access resource
113      *        ids
114      * @throws IllegalArgumentException if the specified random access id list
115      *         is <code>null</code> or empty
116      */
117     public ResourceEditorInput(String editorID, List<String> randomAccessResourceId) {
118         if (randomAccessResourceId == null)
119             throw new IllegalArgumentException("null resource id list");
120         if (randomAccessResourceId.isEmpty())
121             throw new IllegalArgumentException("input resource id list is empty, expected non-empty list");
122         for (String id : randomAccessResourceId)
123             if (id == null)
124                 throw new IllegalArgumentException("input resource id list contains null IDs: " + randomAccessResourceId);
125
126         this.editorID = editorID;
127         if (editorID == null)
128             editorID = "";
129         this.resourceIds = Collections.unmodifiableList(new ArrayList<String>(randomAccessResourceId));
130         this.resources = ResourceInputs.makeReference(ResourceArray.EMPTY);
131
132         setNonExistant();
133     }
134
135     @Override
136     public void init(IAdaptable adapter) throws DatabaseException {
137         // Initialize resource array if at all possible
138         ResourceArray ra = getResourceArray();
139         if (!ra.isEmpty())
140             updateCaches(true);
141     }
142
143     @Override
144     public void dispose() {
145         //System.out.println("dispose resource editor input: " + name);
146         // NOTE: this has to be done since Eclipse will cache these IEditorInput
147         // instances within EditorHistoryItem's that are stored in an EditorHistory
148         // instance. They are held by strong reference which means that the session
149         // cannot be collected if it is not nulled here.
150         resources = null;
151     }
152
153     @Override
154     public boolean exists() {
155         return exists;
156     }
157
158     @Override
159     public boolean exists(ReadGraph graph) throws DatabaseException {
160         for (Resource r : getResourceArray().resources)
161             if (!graph.hasStatement(r))
162                 return false;
163         return true;
164     }
165
166     @Override
167     public Resource getResource() {
168         ResourceArray ra = getResourceArray();
169         return ra.isEmpty() ? null : ra.resources[0];
170     }
171
172     public ResourceArray getResourceArray0() throws DatabaseException {
173         ResourceArray ra = tryGetResourceArray();
174         if (!ra.isEmpty())
175             return ra;
176
177         Session s = ResourceInputs.peekSession();
178         if (s == null)
179             return ResourceArray.EMPTY;
180
181         ra = ResourceInputs.makeResourceArray( s, resourceIds );
182         this.resources = ResourceInputs.makeReference( ra );
183         return ra;
184     }
185
186     @Override
187     public ResourceArray getResourceArray() {
188         try {
189             return getResourceArray0();
190         } catch (DatabaseException e) {
191             ErrorLogger.defaultLogError(e);
192             return ResourceArray.EMPTY;
193         }
194     }
195
196     /* (non-Javadoc)
197      * @see org.eclipse.ui.IEditorInput#getImageDescriptor()
198      */
199     @Override
200     public ImageDescriptor getImageDescriptor() {
201         return imageDesc;
202     }
203
204     /* (non-Javadoc)
205      * @see org.eclipse.ui.IEditorInput#getName()
206      */
207     @Override
208     public String getName() {
209         return name;
210     }
211
212     /* (non-Javadoc)
213      * @see org.eclipse.ui.IEditorInput#getToolTipText()
214      */
215     @Override
216     public String getToolTipText() {
217         return tooltip;
218     }
219
220     /* (non-Javadoc)
221      * @see org.eclipse.ui.IEditorInput#getPersistable()
222      */
223     @Override
224     public IPersistableElement getPersistable() {
225         // Don't allow persistability when it's not possible.
226         if (!isPersistable())
227             return null;
228         return this;
229     }
230
231     protected boolean isPersistable() {
232         Session session = Simantics.peekSession();
233         if (session == null)
234             return false;
235         LifecycleSupport lc = session.peekService(LifecycleSupport.class);
236         if (lc == null)
237             return false;
238         if (lc.isClosed())
239             return false;
240         return true;
241     }
242
243     /* (non-Javadoc)
244      * @see org.eclipse.ui.IPersistableElement#getFactoryId()
245      */
246     @Override
247     public String getFactoryId() {
248         return ResourceEditorInputFactory.getFactoryId();
249     }
250
251     /**
252      * Saves the state of the given resource editor input into the given memento.
253      *
254      * @param memento the storage area for element state
255      * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)
256      */
257     @Override
258     public void saveState(IMemento memento) {
259         for (String id : resourceIds) {
260             IMemento child = memento.createChild(ResourceEditorInputFactory.TAG_RESOURCE_ID);
261             child.putTextData(id);
262         }
263         memento.putString(ResourceEditorInputFactory.TAG_EDITOR_ID, editorID);
264         memento.putString(ResourceEditorInputFactory.TAG_EXTERNAL_MEMENTO_ID, persistentStore.toString());
265     }
266
267     @Override
268     public int hashCode() {
269         final int prime = 31;
270         int result = 1;
271         result = prime * result + editorID.hashCode();
272         result = prime * result + ObjectUtils.hashCode(resourceIds);
273         return result;
274     }
275
276     @Override
277     public boolean equals(Object obj) {
278         if (this == obj)
279             return true;
280         if (obj == null)
281             return false;
282         if (getClass() != obj.getClass())
283             return false;
284         final ResourceEditorInput other = (ResourceEditorInput) obj;
285         if (!editorID.equals(other.editorID))
286             return false;
287         if (!ObjectUtils.objectEquals(resourceIds, other.resourceIds))
288             return false;
289         return true;
290     }
291
292     private void updateCaches(boolean sync) throws DatabaseException {
293         ReadRequest req = new ReadRequest() {
294             @Override
295             public void run(ReadGraph g) throws DatabaseException {
296                 update(g);
297             }
298         };
299         Session s = ResourceInputs.getSession();
300         if (sync) {
301             s.syncRequest(req);
302         } else {
303             s.asyncRequest(req);
304         }
305     }
306
307     /* (non-Javadoc)
308      * @see org.simantics.ui.workbench.IResourceEditorInput#update(org.simantics.db.Graph)
309      */
310     @Override
311     public void update(ReadGraph g) throws DatabaseException {
312         Resource r = getResource();
313         if (r == null)
314             return;
315
316         exists = g.hasStatement(r);
317         if (exists) {
318             name = g.syncRequest(new TitleRequest(editorID, this));
319             if (name == null)
320                 name = NO_NAME;
321
322             tooltip = g.syncRequest(new ToolTipRequest(editorID, this));
323             if (tooltip == null)
324                 tooltip = NO_NAME;
325
326             try {
327                 ImageDescriptorProvider idp = g.adapt(r, ImageDescriptorProvider.class);
328                 imageDesc = idp.get();
329             } catch (AdaptionException e) {
330                 imageDesc = ImageDescriptor.getMissingImageDescriptor();
331             } catch (ProvisionException e) {
332                 imageDesc = ImageDescriptor.getMissingImageDescriptor();
333                 ErrorLogger.defaultLogError(e);
334             }
335         } else {
336             setNonExistant();
337         }
338     }
339
340     private void setNonExistant() {
341         exists = false;
342         tooltip = name = NO_NAME;
343         imageDesc = ImageDescriptor.getMissingImageDescriptor();
344     }
345
346     public IMemento getPersistentStore() {
347         return persistentStore;
348     }
349
350     @Override
351     public String toString() {
352         return getClass().getSimpleName() + " [name=" + getName() + ", resourceIds=" + resourceIds + ", resources=" + resources + "]";
353     }
354
355     private ResourceArray tryGetResourceArray() {
356         Reference<ResourceArray> ref = resources;
357         if (ref == null)
358             return ResourceArray.EMPTY;
359         ResourceArray ra = ref.get();
360         return ra == null ? ResourceArray.EMPTY : ra;
361     }
362
363 }