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