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