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