1 /*******************************************************************************
2 * Copyright (c) 2007, 2013 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - issue #4384
12 *******************************************************************************/
13 package org.simantics.ui.workbench;
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;
39 * A helper class for easing the attachment of a Simantics database session to
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)}.
46 * Works with any IEditorPart but is only really useful with ones that have
49 * @author Tuukka Lehtonen
51 public class ResourceEditorSupport implements IAdaptable, ChangeListener {
53 private IEditorPart editorPart;
55 private ChangeListener editorPartChangeListener;
57 private ISessionContext sessionContext;
59 // Just a cache to make sure that getSession doesn't NPE.
60 private Session session;
62 ParametrizedRead<IResourceEditorInput, Boolean> inputValidator;
64 private InputListener inputListener;
66 public ResourceEditorSupport(IEditorPart editorPart) throws PartInitException {
67 this(editorPart, null);
70 public ResourceEditorSupport(IEditorPart editorPart, ParametrizedRead<IResourceEditorInput, Boolean> inputValidator) throws PartInitException {
71 this.editorPart = editorPart;
72 this.editorPartChangeListener = getChangeListener(editorPart);
73 this.inputValidator = inputValidator;
77 IResourceEditorInput input = getResourceInput(editorPart);
79 throw new PartInitException("Editor input must be an IResourceEditorInput, got " + editorPart.getEditorInput());
83 } catch (DatabaseException e) {
84 throw new PartInitException("Failed to initialize " + input, e);
88 private ISessionContext initSession() throws PartInitException {
89 if (sessionContext == null) {
90 ISessionContextProvider provider = SimanticsUI.getSessionContextProvider();
91 ISessionContext sc = provider.getSessionContext();
93 throw new PartInitException("active database session context is null");
96 session = sc.getSession();
98 if (editorPartChangeListener != null) {
99 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
100 support.addListener(this);
103 return sessionContext;
106 private ChangeListener getChangeListener(IEditorPart part) {
107 if (part instanceof ChangeListener)
108 return (ChangeListener) part;
109 ChangeListener cl = (ChangeListener) part.getAdapter(ChangeListener.class);
113 private static IResourceEditorInput getResourceInput(IEditorPart part) {
114 IEditorInput input = part.getEditorInput();
115 if (input instanceof IResourceEditorInput)
116 return (IResourceEditorInput) input;
120 public void dispose() {
121 deactivateValidation();
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);
132 sessionContext = null;
133 if (session != null) {
134 if (editorPartChangeListener != null) {
135 GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class);
136 support.removeListener(this);
141 editorPartChangeListener = null;
144 protected boolean isDisposed() {
145 return editorPart == null;
148 public synchronized void activateValidation() {
150 throw new IllegalStateException(this + " is disposed");
151 if (inputListener != null)
154 inputListener = new InputListener();
155 getSession().asyncRequest(validationRequest(editorPart), inputListener);
158 public synchronized void deactivateValidation() {
160 throw new IllegalStateException(this + " is disposed");
161 if (inputListener == null)
163 inputListener.dispose();
166 public ISessionContext getSessionContext() {
167 if (sessionContext == null)
168 throw new IllegalStateException("ResourceEditorSupport is disposed");
169 return sessionContext;
172 public Session getSession() {
174 throw new IllegalStateException("ResourceEditorSupport is disposed");
178 @SuppressWarnings("rawtypes")
180 public Object getAdapter(Class adapter) {
181 if (adapter == ISessionContext.class)
182 return getSessionContext();
183 if (adapter == Session.class)
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);
197 static enum InputState {
202 public static InputState parse(boolean exists, boolean valid) {
205 return valid ? VALID : INVALID;
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);
214 public IEditorPart getEditorPart() {
215 return (IEditorPart) getField(0);
218 public IEditorInput getEditorInput() {
219 return (IEditorInput) getField(1);
222 public InputState getInputState() {
223 return (InputState) getField(2);
229 * @return a read request that returns <code>true</code> for valid inputs
230 * and <code>false</code> for non-existent or invalid inputs.
232 private Read<Evaluation> validationRequest(IEditorPart editorPart) {
233 return new UnaryRead<IEditorPart, Evaluation>(editorPart) {
235 public Evaluation perform(ReadGraph graph) throws DatabaseException {
236 IEditorInput input = parameter.getEditorInput();
237 IResourceEditorInput resourceInput = getResourceInput(parameter);
239 //System.out.println(ResourceEditorSupport.this + ": checking input " + input);
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));
249 exists = input.exists();
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);
258 Evaluation eval = new Evaluation(parameter, input, state, input.getName(), input.getToolTipText());
259 //System.out.println(ResourceEditorSupport.this + ": validation evaluation: " + eval);
265 private class InputListener extends ListenerAdapter<Evaluation> {
267 private boolean disposed = false;
269 public void dispose() {
274 public void execute(Evaluation evaluation) {
275 //System.out.println("InputListener: " + evaluation);
276 switch (evaluation.getInputState()) {
282 scheduleEditorClose(evaluation.getEditorPart());
288 public void exception(Throwable t) {
289 ExceptionUtils.logError("ResourceEditorSupport.InputListener received an unexpected exception.", t);
293 public boolean isDisposed() {
294 return disposed || ResourceEditorSupport.this.isDisposed();
298 private void scheduleEditorClose(final IEditorPart editorPart) {
299 SWTUtils.asyncExec(editorPart.getSite().getShell(), new Runnable() {
302 // Don't have to check isDisposed since closeEditor
303 // will ignore already closed editor parts.
304 WorkbenchUtils.closeEditor(editorPart, false);