X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.ui%2Fsrc%2Forg%2Fsimantics%2Fui%2Fworkbench%2FResourceEditorSupport.java;h=3597804d8f83164637e8f0b5e60e44c9879d25c2;hp=f649bb53874d8586c44fc64a899bb22cd3b2b1a9;hb=e88be95edf1f80781646cfdf717ec1b663264179;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java index f649bb538..3597804d8 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java @@ -1,309 +1,323 @@ -/******************************************************************************* - * Copyright (c) 2007, 2013 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - * Semantum Oy - issue #4384 - *******************************************************************************/ -package org.simantics.ui.workbench; - -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.PlatformUI; -import org.simantics.db.ReadGraph; -import org.simantics.db.Session; -import org.simantics.db.common.procedure.adapter.ListenerAdapter; -import org.simantics.db.common.request.ParametrizedRead; -import org.simantics.db.common.request.UnaryRead; -import org.simantics.db.event.ChangeEvent; -import org.simantics.db.event.ChangeListener; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.management.ISessionContext; -import org.simantics.db.management.ISessionContextProvider; -import org.simantics.db.request.Read; -import org.simantics.db.service.GraphChangeListenerSupport; -import org.simantics.ui.SimanticsUI; -import org.simantics.utils.datastructures.map.Tuple; -import org.simantics.utils.ui.ExceptionUtils; -import org.simantics.utils.ui.SWTUtils; -import org.simantics.utils.ui.workbench.WorkbenchUtils; - -/** - * A helper class for easing the attachment of a Simantics database session to - * an editor part. - * - * It handles the life-cycle of {@link IResourceEditorInput} inputs. It will - * listen to graph database changes through {@link ChangeListener} and calls - * {@link IResourceEditorInput#update(org.simantics.db.ReadGraph)}. - * - * Works with any IEditorPart but is only really useful with ones that have - * resource inputs. - * - * @author Tuukka Lehtonen - */ -public class ResourceEditorSupport implements IAdaptable, ChangeListener { - - private IEditorPart editorPart; - - private ChangeListener editorPartChangeListener; - - private ISessionContext sessionContext; - - // Just a cache to make sure that getSession doesn't NPE. - private Session session; - - ParametrizedRead inputValidator; - - private InputListener inputListener; - - public ResourceEditorSupport(IEditorPart editorPart) throws PartInitException { - this(editorPart, null); - } - - public ResourceEditorSupport(IEditorPart editorPart, ParametrizedRead inputValidator) throws PartInitException { - this.editorPart = editorPart; - this.editorPartChangeListener = getChangeListener(editorPart); - this.inputValidator = inputValidator; - - initSession(); - - IResourceEditorInput input = getResourceInput(editorPart); - if (input == null) - throw new PartInitException("Editor input must be an IResourceEditorInput, got " + editorPart.getEditorInput()); - - try { - input.init(this); - } catch (DatabaseException e) { - throw new PartInitException("Failed to initialize " + input, e); - } - } - - private ISessionContext initSession() throws PartInitException { - if (sessionContext == null) { - ISessionContextProvider provider = SimanticsUI.getSessionContextProvider(); - ISessionContext sc = provider.getSessionContext(); - if (sc == null) - throw new PartInitException("active database session context is null"); - - sessionContext = sc; - session = sc.getSession(); - - if (editorPartChangeListener != null) { - GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class); - support.addListener(this); - } - } - return sessionContext; - } - - private ChangeListener getChangeListener(IEditorPart part) { - if (part instanceof ChangeListener) - return (ChangeListener) part; - ChangeListener cl = (ChangeListener) part.getAdapter(ChangeListener.class); - return cl; - } - - private static IResourceEditorInput getResourceInput(IEditorPart part) { - IEditorInput input = part.getEditorInput(); - if (input instanceof IResourceEditorInput) - return (IResourceEditorInput) input; - return null; - } - - public void dispose() { - deactivateValidation(); - - // This is special for IResourceInput, has to be done in order not to - // leak random access id's for resources. - if (!PlatformUI.getWorkbench().isClosing()) { - IResourceEditorInput input = getResourceInput(editorPart); - if (input != null) - input.dispose(); - } - editorPart = null; - - sessionContext = null; - if (session != null) { - if (editorPartChangeListener != null) { - GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class); - support.removeListener(this); - } - session = null; - } - - editorPartChangeListener = null; - } - - protected boolean isDisposed() { - return editorPart == null; - } - - public synchronized void activateValidation() { - if (isDisposed()) - throw new IllegalStateException(this + " is disposed"); - if (inputListener != null) - return; - - inputListener = new InputListener(); - getSession().asyncRequest(validationRequest(editorPart), inputListener); - } - - public synchronized void deactivateValidation() { - if (isDisposed()) - throw new IllegalStateException(this + " is disposed"); - if (inputListener == null) - return; - inputListener.dispose(); - } - - public ISessionContext getSessionContext() { - if (sessionContext == null) - throw new IllegalStateException("ResourceEditorSupport is disposed"); - return sessionContext; - } - - public Session getSession() { - if (session == null) - throw new IllegalStateException("ResourceEditorSupport is disposed"); - return session; - } - - @SuppressWarnings("rawtypes") - @Override - public Object getAdapter(Class adapter) { - if (adapter == ISessionContext.class) - return getSessionContext(); - if (adapter == Session.class) - return getSession(); - return null; - } - - @Override - public void graphChanged(ChangeEvent e) throws DatabaseException { - //System.out.println(this + ": graph change: " + e); - // Only forward the update to the editor if the input is still valid and - // the editor implements ChangeListener - if (editorPart instanceof ChangeListener) - ((ChangeListener) editorPart).graphChanged(e); - } - - static enum InputState { - VALID, - INVALID, - NON_EXISTENT; - - public static InputState parse(boolean exists, boolean valid) { - if (!exists) - return NON_EXISTENT; - return valid ? VALID : INVALID; - } - } - - static class Evaluation extends Tuple { - public Evaluation(IEditorPart editorPart, IEditorInput input, InputState state, String name, String tooltip) { - super(editorPart, input, state, name, tooltip); - } - - public IEditorPart getEditorPart() { - return (IEditorPart) getField(0); - } - - public IEditorInput getEditorInput() { - return (IEditorInput) getField(1); - } - - public InputState getInputState() { - return (InputState) getField(2); - } - } - - /** - * @param input - * @return a read request that returns true for valid inputs - * and false for non-existent or invalid inputs. - */ - private Read validationRequest(IEditorPart editorPart) { - return new UnaryRead(editorPart) { - @Override - public Evaluation perform(ReadGraph graph) throws DatabaseException { - IEditorInput input = parameter.getEditorInput(); - IResourceEditorInput resourceInput = getResourceInput(parameter); - - //System.out.println(ResourceEditorSupport.this + ": checking input " + input); - - boolean exists = true; - boolean valid = true; - if (resourceInput != null) { - exists = resourceInput.exists(graph); - if (exists && inputValidator != null) { - valid = graph.syncRequest(inputValidator.get(resourceInput)); - } - } else { - exists = input.exists(); - } - - InputState state = InputState.parse(exists, valid); - if (state == InputState.VALID) { - // Make sure any cached data in the editor input is up-to-date. - resourceInput.update(graph); - } - - Evaluation eval = new Evaluation(parameter, input, state, input.getName(), input.getToolTipText()); - //System.out.println(ResourceEditorSupport.this + ": validation evaluation: " + eval); - return eval; - } - }; - } - - private class InputListener extends ListenerAdapter { - - private boolean disposed = false; - - public void dispose() { - disposed = true; - } - - @Override - public void execute(Evaluation evaluation) { - //System.out.println("InputListener: " + evaluation); - switch (evaluation.getInputState()) { - case VALID: - break; - - case INVALID: - case NON_EXISTENT: - scheduleEditorClose(evaluation.getEditorPart()); - break; - } - } - - @Override - public void exception(Throwable t) { - ExceptionUtils.logError("ResourceEditorSupport.InputListener received an unexpected exception.", t); - } - - @Override - public boolean isDisposed() { - return disposed || ResourceEditorSupport.this.isDisposed(); - } - } - - private void scheduleEditorClose(final IEditorPart editorPart) { - SWTUtils.asyncExec(editorPart.getSite().getShell(), new Runnable() { - @Override - public void run() { - // Don't have to check isDisposed since closeEditor - // will ignore already closed editor parts. - WorkbenchUtils.closeEditor(editorPart, false); - } - }); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2013 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + * Semantum Oy - issue #4384 + *******************************************************************************/ +package org.simantics.ui.workbench; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Session; +import org.simantics.db.common.procedure.adapter.ListenerAdapter; +import org.simantics.db.common.request.ParametrizedRead; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.event.ChangeEvent; +import org.simantics.db.event.ChangeListener; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.management.ISessionContextProvider; +import org.simantics.db.service.GraphChangeListenerSupport; +import org.simantics.utils.datastructures.map.Tuple; +import org.simantics.utils.ui.ExceptionUtils; +import org.simantics.utils.ui.SWTUtils; +import org.simantics.utils.ui.workbench.WorkbenchUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A helper class for easing the attachment of a Simantics database session to + * an editor part. + * + * It handles the life-cycle of {@link IResourceEditorInput} inputs. It will + * listen to graph database changes through {@link ChangeListener} and calls + * {@link IResourceEditorInput#update(org.simantics.db.ReadGraph)}. + * + * Works with any IEditorPart but is only really useful with ones that have + * resource inputs. + * + * @author Tuukka Lehtonen + */ +public class ResourceEditorSupport implements IAdaptable, ChangeListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(ResourceEditorSupport.class); + private static final boolean DEBUG = false; + + private IEditorPart editorPart; + + private ChangeListener editorPartChangeListener; + + private ISessionContext sessionContext; + + // Just a cache to make sure that getSession doesn't NPE. + private Session session; + + ParametrizedRead inputValidator; + + private InputListener inputListener; + + public ResourceEditorSupport(IEditorPart editorPart) throws PartInitException { + this(editorPart, null); + } + + public ResourceEditorSupport(IEditorPart editorPart, ParametrizedRead inputValidator) throws PartInitException { + this.editorPart = editorPart; + this.editorPartChangeListener = getChangeListener(editorPart); + this.inputValidator = inputValidator; + + initSession(); + + IResourceEditorInput input = getResourceInput(editorPart); + if (input == null) + throw new PartInitException("Editor input must be an IResourceEditorInput, got " + editorPart.getEditorInput()); + + try { + input.init(this); + } catch (DatabaseException e) { + throw new PartInitException("Failed to initialize " + input, e); + } + } + + private ISessionContext initSession() throws PartInitException { + if (sessionContext == null) { + ISessionContextProvider provider = Simantics.getSessionContextProvider(); + ISessionContext sc = provider.getSessionContext(); + if (sc == null) + throw new PartInitException("active database session context is null"); + + sessionContext = sc; + session = sc.getSession(); + + if (editorPartChangeListener != null) { + GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class); + support.addListener(this); + } + } + return sessionContext; + } + + private ChangeListener getChangeListener(IEditorPart part) { + if (part instanceof ChangeListener) + return (ChangeListener) part; + ChangeListener cl = (ChangeListener) part.getAdapter(ChangeListener.class); + return cl; + } + + private static IResourceEditorInput getResourceInput(IEditorPart part) { + IEditorInput input = part.getEditorInput(); + if (input instanceof IResourceEditorInput) + return (IResourceEditorInput) input; + return null; + } + + public void dispose() { + deactivateValidation(); + + // This is special for IResourceInput, has to be done in order not to + // leak random access id's for resources. + if (!PlatformUI.getWorkbench().isClosing()) { + IResourceEditorInput input = getResourceInput(editorPart); + if (input != null) + input.dispose(); + } + editorPart = null; + + sessionContext = null; + if (session != null) { + if (editorPartChangeListener != null) { + GraphChangeListenerSupport support = session.getService(GraphChangeListenerSupport.class); + support.removeListener(this); + } + session = null; + } + + editorPartChangeListener = null; + } + + protected boolean isDisposed() { + return editorPart == null; + } + + public synchronized void activateValidation() { + if (isDisposed()) + throw new IllegalStateException(this + " is disposed"); + if (inputListener != null) + return; + + inputListener = new InputListener(); + getSession().asyncRequest(new ValidationRequest(), inputListener); + } + + public synchronized void deactivateValidation() { + if (isDisposed()) + throw new IllegalStateException(this + " is disposed"); + if (inputListener == null) + return; + inputListener.dispose(); + inputListener = null; + } + + public ISessionContext getSessionContext() { + if (sessionContext == null) + throw new IllegalStateException("ResourceEditorSupport is disposed"); + return sessionContext; + } + + public Session getSession() { + if (session == null) + throw new IllegalStateException("ResourceEditorSupport is disposed"); + return session; + } + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) { + if (adapter == ISessionContext.class) + return (T) getSessionContext(); + if (adapter == Session.class) + return (T) getSession(); + return null; + } + + @Override + public void graphChanged(ChangeEvent e) throws DatabaseException { + // Only forward the update to the editor if the input is still valid and + // the editor implements ChangeListener + if (editorPart instanceof ChangeListener) + ((ChangeListener) editorPart).graphChanged(e); + } + + static enum InputState { + VALID, + INVALID, + NON_EXISTENT; + + public static InputState parse(boolean exists, boolean valid) { + if (!exists) + return NON_EXISTENT; + return valid ? VALID : INVALID; + } + } + + static class Evaluation extends Tuple { + public Evaluation(IEditorPart editorPart, IEditorInput input, InputState state, String name, String tooltip) { + super(editorPart, input, state, name, tooltip); + } + + public IEditorPart getEditorPart() { + return (IEditorPart) getField(0); + } + + public IEditorInput getEditorInput() { + return (IEditorInput) getField(1); + } + + public InputState getInputState() { + return (InputState) getField(2); + } + } + + /** + * A read request that returns an {@link Evaluation} of the current state of + * editorPart. + * + *

+ * This request class is not static but has no parameters that could get + * stuck in the database client caches. UniqueRead does not need arguments + * and without custom hashCode/equals implementations, each instance of this + * request is a different one. This is exactly the behaviour we want in this + * case. + */ + private class ValidationRequest extends UniqueRead { + @Override + public Evaluation perform(ReadGraph graph) throws DatabaseException { + IEditorPart part = editorPart; + if (part == null) + return new Evaluation(null, null, InputState.INVALID, "", ""); + + IEditorInput input = part.getEditorInput(); + IResourceEditorInput resourceInput = getResourceInput(part); + + if (DEBUG) + LOGGER.trace("ValidationRequest: checking input " + input); + + boolean exists = true; + boolean valid = true; + if (resourceInput != null) { + exists = resourceInput.exists(graph); + if (exists && inputValidator != null) { + valid = graph.syncRequest(inputValidator.get(resourceInput)); + } + } else { + exists = input.exists(); + } + + InputState state = InputState.parse(exists, valid); + if (state == InputState.VALID) { + // Make sure any cached data in the editor input is up-to-date. + if (resourceInput != null) + resourceInput.update(graph); + } + + Evaluation eval = new Evaluation(part, input, state, input.getName(), input.getToolTipText()); + if (DEBUG) + LOGGER.trace("ValidationRequest: evaluation result: " + eval); + return eval; + } + } + + private static class InputListener extends ListenerAdapter { + + private boolean disposed = false; + + public void dispose() { + disposed = true; + } + + @Override + public void execute(Evaluation evaluation) { + if (DEBUG) + LOGGER.trace("InputListener: " + evaluation); + switch (evaluation.getInputState()) { + case VALID: + break; + case INVALID: + case NON_EXISTENT: + scheduleEditorClose(evaluation.getEditorPart()); + break; + } + } + + @Override + public void exception(Throwable t) { + ExceptionUtils.logError("ResourceEditorSupport.InputListener received an unexpected exception.", t); + } + + @Override + public boolean isDisposed() { + return disposed; + } + } + + private static void scheduleEditorClose(IEditorPart editorPart) { + if (editorPart == null) + return; + SWTUtils.asyncExec(editorPart.getSite().getWorkbenchWindow().getShell(), () -> { + // Don't have to check isDisposed since closeEditor + // will ignore already closed editor parts. + WorkbenchUtils.closeEditor(editorPart, false); + }); + } + +}