]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java
Remove usage of deprecated SimanticsUI-methods
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / workbench / ResourceEditorSupport.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 org.eclipse.core.runtime.IAdaptable;
16 import org.eclipse.ui.IEditorInput;
17 import org.eclipse.ui.IEditorPart;
18 import org.eclipse.ui.PartInitException;
19 import org.eclipse.ui.PlatformUI;
20 import org.simantics.Simantics;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.Session;
23 import org.simantics.db.common.procedure.adapter.ListenerAdapter;
24 import org.simantics.db.common.request.ParametrizedRead;
25 import org.simantics.db.common.request.UniqueRead;
26 import org.simantics.db.event.ChangeEvent;
27 import org.simantics.db.event.ChangeListener;
28 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.db.management.ISessionContext;
30 import org.simantics.db.management.ISessionContextProvider;
31 import org.simantics.db.service.GraphChangeListenerSupport;
32 import org.simantics.utils.datastructures.map.Tuple;
33 import org.simantics.utils.ui.ExceptionUtils;
34 import org.simantics.utils.ui.SWTUtils;
35 import org.simantics.utils.ui.workbench.WorkbenchUtils;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * A helper class for easing the attachment of a Simantics database session to
41  * an editor part.
42  * 
43  * It handles the life-cycle of {@link IResourceEditorInput} inputs. It will
44  * listen to graph database changes through {@link ChangeListener} and calls
45  * {@link IResourceEditorInput#update(org.simantics.db.ReadGraph)}.
46  * 
47  * Works with any IEditorPart but is only really useful with ones that have
48  * resource inputs.
49  * 
50  * @author Tuukka Lehtonen
51  */
52 public class ResourceEditorSupport implements IAdaptable, ChangeListener {
53
54     private static final Logger LOGGER = LoggerFactory.getLogger(ResourceEditorSupport.class);
55     private static final boolean DEBUG = false;
56
57     private IEditorPart                             editorPart;
58
59     private ChangeListener                          editorPartChangeListener;
60
61     private ISessionContext                         sessionContext;
62
63     // Just a cache to make sure that getSession doesn't NPE.
64     private Session                                 session;
65
66     ParametrizedRead<IResourceEditorInput, Boolean> inputValidator;
67
68     private InputListener                           inputListener;
69
70     public ResourceEditorSupport(IEditorPart editorPart) throws PartInitException {
71         this(editorPart, null);
72     }
73
74     public ResourceEditorSupport(IEditorPart editorPart, ParametrizedRead<IResourceEditorInput, Boolean> inputValidator) throws PartInitException {
75         this.editorPart = editorPart;
76         this.editorPartChangeListener = getChangeListener(editorPart);
77         this.inputValidator = inputValidator;
78
79         initSession();
80
81         IResourceEditorInput input = getResourceInput(editorPart);
82         if (input == null)
83             throw new PartInitException("Editor input must be an IResourceEditorInput, got " + editorPart.getEditorInput());
84
85         try {
86             input.init(this);
87         } catch (DatabaseException e) {
88             throw new PartInitException("Failed to initialize " + input, e);
89         }
90     }
91
92     private ISessionContext initSession() throws PartInitException {
93         if (sessionContext == null) {
94             ISessionContextProvider provider = Simantics.getSessionContextProvider();
95             ISessionContext sc = provider.getSessionContext();
96             if (sc == null)
97                 throw new PartInitException("active database session context is null");
98
99             sessionContext = sc;
100             session = sc.getSession();
101
102             if (editorPartChangeListener != null) {
103                 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
104                 support.addListener(this);
105             }
106         }
107         return sessionContext;
108     }
109
110     private ChangeListener getChangeListener(IEditorPart part) {
111         if (part instanceof ChangeListener)
112             return (ChangeListener) part;
113         ChangeListener cl = (ChangeListener) part.getAdapter(ChangeListener.class);
114         return cl;
115     }
116
117     private static IResourceEditorInput getResourceInput(IEditorPart part) {
118         IEditorInput input = part.getEditorInput();
119         if (input instanceof IResourceEditorInput)
120             return (IResourceEditorInput) input;
121         return null;
122     }
123
124     public void dispose() {
125         deactivateValidation();
126
127         // This is special for IResourceInput, has to be done in order not to
128         // leak random access id's for resources.
129         if (!PlatformUI.getWorkbench().isClosing()) {
130             IResourceEditorInput input = getResourceInput(editorPart);
131             if (input != null)
132                 input.dispose();
133         }
134         editorPart = null;
135
136         sessionContext = null;
137         if (session != null) {
138             if (editorPartChangeListener != null) {
139                 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
140                 support.removeListener(this);
141             }
142             session = null;
143         }
144
145         editorPartChangeListener = null;
146     }
147
148     protected boolean isDisposed() {
149         return editorPart == null;
150     }
151
152     public synchronized void activateValidation() {
153         if (isDisposed())
154             throw new IllegalStateException(this + " is disposed");
155         if (inputListener != null)
156             return;
157
158         inputListener = new InputListener();
159         getSession().asyncRequest(new ValidationRequest(), inputListener);
160     }
161
162     public synchronized void deactivateValidation() {
163         if (isDisposed())
164             throw new IllegalStateException(this + " is disposed");
165         if (inputListener == null)
166             return;
167         inputListener.dispose();
168         inputListener = null;
169     }
170
171     public ISessionContext getSessionContext() {
172         if (sessionContext == null)
173             throw new IllegalStateException("ResourceEditorSupport is disposed");
174         return sessionContext;
175     }
176
177     public Session getSession() {
178         if (session == null)
179             throw new IllegalStateException("ResourceEditorSupport is disposed");
180         return session;
181     }
182
183     @SuppressWarnings("unchecked")
184     @Override
185     public <T> T getAdapter(Class<T> adapter) {
186         if (adapter == ISessionContext.class)
187             return (T) getSessionContext();
188         if (adapter == Session.class)
189             return (T) getSession();
190         return null;
191     }
192
193     @Override
194     public void graphChanged(ChangeEvent e) throws DatabaseException {
195         // Only forward the update to the editor if the input is still valid and
196         // the editor implements ChangeListener
197         if (editorPart instanceof ChangeListener)
198             ((ChangeListener) editorPart).graphChanged(e);
199     }
200
201     static enum InputState {
202         VALID,
203         INVALID,
204         NON_EXISTENT;
205
206         public static InputState parse(boolean exists, boolean valid) {
207             if (!exists)
208                 return NON_EXISTENT;
209             return valid ? VALID : INVALID;
210         }
211     }
212
213     static class Evaluation extends Tuple {
214         public Evaluation(IEditorPart editorPart, IEditorInput input, InputState state, String name, String tooltip) {
215             super(editorPart, input, state, name, tooltip);
216         }
217
218         public IEditorPart getEditorPart() {
219             return (IEditorPart) getField(0);
220         }
221
222         public IEditorInput getEditorInput() {
223             return (IEditorInput) getField(1);
224         }
225
226         public InputState getInputState() {
227             return (InputState) getField(2);
228         }
229     }
230
231     /**
232      * A read request that returns an {@link Evaluation} of the current state of
233      * <code>editorPart</code>.
234      * 
235      * <p>
236      * This request class is not static but has no parameters that could get
237      * stuck in the database client caches. UniqueRead does not need arguments
238      * and without custom hashCode/equals implementations, each instance of this
239      * request is a different one. This is exactly the behaviour we want in this
240      * case.
241      */
242     private class ValidationRequest extends UniqueRead<Evaluation> {
243         @Override
244         public Evaluation perform(ReadGraph graph) throws DatabaseException {
245             IEditorPart part = editorPart;
246             if (part == null)
247                 return new Evaluation(null, null, InputState.INVALID, "", "");
248
249             IEditorInput input = part.getEditorInput();
250             IResourceEditorInput resourceInput = getResourceInput(part);
251
252             if (DEBUG)
253                 LOGGER.trace("ValidationRequest: checking input " + input);
254
255             boolean exists = true;
256             boolean valid = true;
257             if (resourceInput != null) {
258                 exists = resourceInput.exists(graph);
259                 if (exists && inputValidator != null) {
260                     valid = graph.syncRequest(inputValidator.get(resourceInput));
261                 }
262             } else {
263                 exists = input.exists();
264             }
265
266             InputState state = InputState.parse(exists, valid);
267             if (state == InputState.VALID) {
268                 // Make sure any cached data in the editor input is up-to-date.
269                 if (resourceInput != null)
270                     resourceInput.update(graph);
271             }
272
273             Evaluation eval = new Evaluation(part, input, state, input.getName(), input.getToolTipText());
274             if (DEBUG)
275                 LOGGER.trace("ValidationRequest: evaluation result: " + eval);
276             return eval;
277         }
278     }
279
280     private static class InputListener extends ListenerAdapter<Evaluation> {
281
282         private boolean disposed = false;
283
284         public void dispose() {
285             disposed = true;
286         }
287
288         @Override
289         public void execute(Evaluation evaluation) {
290             if (DEBUG)
291                 LOGGER.trace("InputListener: " + evaluation);
292             switch (evaluation.getInputState()) {
293                 case VALID:
294                     break;
295                 case INVALID:
296                 case NON_EXISTENT:
297                     scheduleEditorClose(evaluation.getEditorPart());
298                     break;
299             }
300         }
301
302         @Override
303         public void exception(Throwable t) {
304             ExceptionUtils.logError("ResourceEditorSupport.InputListener received an unexpected exception.", t);
305         }
306
307         @Override
308         public boolean isDisposed() {
309             return disposed;
310         }
311     }
312
313     private static void scheduleEditorClose(IEditorPart editorPart) {
314         if (editorPart == null)
315             return;
316         SWTUtils.asyncExec(editorPart.getSite().getWorkbenchWindow().getShell(), () -> {
317             // Don't have to check isDisposed since closeEditor
318             // will ignore already closed editor parts.
319             WorkbenchUtils.closeEditor(editorPart, false);
320         });
321     }
322
323 }