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;fp=bundles%2Forg.simantics.ui%2Fsrc%2Forg%2Fsimantics%2Fui%2Fworkbench%2FResourceEditorSupport.java;h=f649bb53874d8586c44fc64a899bb22cd3b2b1a9;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 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 new file mode 100644 index 000000000..f649bb538 --- /dev/null +++ b/bundles/org.simantics.ui/src/org/simantics/ui/workbench/ResourceEditorSupport.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * 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); + } + }); + } + +}