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
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
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
39 * A helper class for easing the attachment of a Simantics database session to
\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
46 * Works with any IEditorPart but is only really useful with ones that have
\r
49 * @author Tuukka Lehtonen
\r
51 public class ResourceEditorSupport implements IAdaptable, ChangeListener {
\r
53 private IEditorPart editorPart;
\r
55 private ChangeListener editorPartChangeListener;
\r
57 private ISessionContext sessionContext;
\r
59 // Just a cache to make sure that getSession doesn't NPE.
\r
60 private Session session;
\r
62 ParametrizedRead<IResourceEditorInput, Boolean> inputValidator;
\r
64 private InputListener inputListener;
\r
66 public ResourceEditorSupport(IEditorPart editorPart) throws PartInitException {
\r
67 this(editorPart, null);
\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
77 IResourceEditorInput input = getResourceInput(editorPart);
\r
79 throw new PartInitException("Editor input must be an IResourceEditorInput, got " + editorPart.getEditorInput());
\r
83 } catch (DatabaseException e) {
\r
84 throw new PartInitException("Failed to initialize " + input, e);
\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
93 throw new PartInitException("active database session context is null");
\r
95 sessionContext = sc;
\r
96 session = sc.getSession();
\r
98 if (editorPartChangeListener != null) {
\r
99 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
\r
100 support.addListener(this);
\r
103 return sessionContext;
\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
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
120 public void dispose() {
\r
121 deactivateValidation();
\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
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
141 editorPartChangeListener = null;
\r
144 protected boolean isDisposed() {
\r
145 return editorPart == null;
\r
148 public synchronized void activateValidation() {
\r
150 throw new IllegalStateException(this + " is disposed");
\r
151 if (inputListener != null)
\r
154 inputListener = new InputListener();
\r
155 getSession().asyncRequest(validationRequest(editorPart), inputListener);
\r
158 public synchronized void deactivateValidation() {
\r
160 throw new IllegalStateException(this + " is disposed");
\r
161 if (inputListener == null)
\r
163 inputListener.dispose();
\r
166 public ISessionContext getSessionContext() {
\r
167 if (sessionContext == null)
\r
168 throw new IllegalStateException("ResourceEditorSupport is disposed");
\r
169 return sessionContext;
\r
172 public Session getSession() {
\r
173 if (session == null)
\r
174 throw new IllegalStateException("ResourceEditorSupport is disposed");
\r
178 @SuppressWarnings("rawtypes")
\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
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
197 static enum InputState {
\r
202 public static InputState parse(boolean exists, boolean valid) {
\r
204 return NON_EXISTENT;
\r
205 return valid ? VALID : INVALID;
\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
214 public IEditorPart getEditorPart() {
\r
215 return (IEditorPart) getField(0);
\r
218 public IEditorInput getEditorInput() {
\r
219 return (IEditorInput) getField(1);
\r
222 public InputState getInputState() {
\r
223 return (InputState) getField(2);
\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
232 private Read<Evaluation> validationRequest(IEditorPart editorPart) {
\r
233 return new UnaryRead<IEditorPart, Evaluation>(editorPart) {
\r
235 public Evaluation perform(ReadGraph graph) throws DatabaseException {
\r
236 IEditorInput input = parameter.getEditorInput();
\r
237 IResourceEditorInput resourceInput = getResourceInput(parameter);
\r
239 //System.out.println(ResourceEditorSupport.this + ": checking input " + input);
\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
249 exists = input.exists();
\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
258 Evaluation eval = new Evaluation(parameter, input, state, input.getName(), input.getToolTipText());
\r
259 //System.out.println(ResourceEditorSupport.this + ": validation evaluation: " + eval);
\r
265 private class InputListener extends ListenerAdapter<Evaluation> {
\r
267 private boolean disposed = false;
\r
269 public void dispose() {
\r
274 public void execute(Evaluation evaluation) {
\r
275 //System.out.println("InputListener: " + evaluation);
\r
276 switch (evaluation.getInputState()) {
\r
282 scheduleEditorClose(evaluation.getEditorPart());
\r
288 public void exception(Throwable t) {
\r
289 ExceptionUtils.logError("ResourceEditorSupport.InputListener received an unexpected exception.", t);
\r
293 public boolean isDisposed() {
\r
294 return disposed || ResourceEditorSupport.this.isDisposed();
\r
298 private void scheduleEditorClose(final IEditorPart editorPart) {
\r
299 SWTUtils.asyncExec(editorPart.getSite().getShell(), new Runnable() {
\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