]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/DeleteHandler.java
Add progress monitoring for DeleteHandler
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / DeleteHandler.java
index d26e0cdcdaf27788286c16bb255119df70c33503..5fa94ebc7a2787d1c9b3a51db9fe508ef04106dc 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 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
- *******************************************************************************/\r
-package org.simantics.diagram.handler;\r
-\r
-import java.util.ArrayDeque;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Deque;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.jface.action.IStatusLineManager;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.simantics.DatabaseJob;\r
-import org.simantics.Simantics;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.adapter.Remover;\r
-import org.simantics.db.layer0.exception.CannotRemoveException;\r
-import org.simantics.db.layer0.util.RemoverUtil;\r
-import org.simantics.diagram.adapter.ElementFactoryUtil;\r
-import org.simantics.diagram.content.ConnectionUtil;\r
-import org.simantics.diagram.content.EdgeResource;\r
-import org.simantics.diagram.internal.Activator;\r
-import org.simantics.diagram.synchronization.ISynchronizationContext;\r
-import org.simantics.diagram.synchronization.graph.RemoveBranchpoint;\r
-import org.simantics.diagram.synchronization.graph.RemoveElement;\r
-import org.simantics.diagram.ui.DiagramModelHints;\r
-import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
-import org.simantics.g2d.connection.ConnectionEntity;\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;\r
-import org.simantics.g2d.diagram.handler.Relationship;\r
-import org.simantics.g2d.diagram.handler.RelationshipHandler;\r
-import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;\r
-import org.simantics.g2d.diagram.handler.Topology;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
-import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;\r
-import org.simantics.g2d.diagram.participant.Selection;\r
-import org.simantics.g2d.element.ElementClass;\r
-import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
-import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
-import org.simantics.scenegraph.g2d.events.command.Commands;\r
-import org.simantics.utils.logging.TimeLogger;\r
-import org.simantics.utils.strings.EString;\r
-import org.simantics.utils.threads.SWTThread;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-import org.simantics.utils.ui.dialogs.ShowMessage;\r
-\r
-/**\r
- * DeleteHandler is a canvas handler for Commands.DELETE commands for an\r
- * IDiagram.\r
- * \r
- * <p>\r
- * The handler attempts to delete the current selection for pointer 0, meaning\r
- * {@link Selection#SELECTION0}.\r
- * </p>\r
- * \r
- * <p>\r
- * The handler logic goes as follows:\r
- * </p>\r
- * <ol>\r
- * <li>Separate nodes and edges form the the removed selection</li>\r
- * <li>Find all edges attached to the removed nodes and remove them too</li>\r
- * <li>Delete connections that contain less than 2 terminal connections</li>\r
- * </ol>\r
- * \r
- * @see Selection for the current diagram selection source\r
- * \r
- * @author Tuukka Lehtonen\r
- * \r
- * TODO: start using WorkbenchStatusLine participant\r
- */\r
-public class DeleteHandler extends AbstractDiagramParticipant {\r
-\r
-    public static final boolean DEBUG_DELETE = false;\r
-\r
-    @Dependency Selection sel;\r
-\r
-    private final IStatusLineManager statusLine;\r
-\r
-    public DeleteHandler(IStatusLineManager statusLine) {\r
-        this.statusLine = statusLine;\r
-    }\r
-\r
-    @EventHandler(priority = 0)\r
-    public boolean handleCommand(CommandEvent e) {\r
-        if (Commands.DELETE.equals( e.command )) {\r
-            IDiagram d = diagram;\r
-            if (d == null)\r
-                return true;\r
-\r
-            Set<IElement> ss = sel.getSelection(0);\r
-            if (ss.isEmpty())\r
-                return true;\r
-\r
-            if (delete(d, ss)) {\r
-                sel.clear(0);\r
-            }\r
-\r
-            return true;\r
-        }\r
-        return false;\r
-    }\r
-\r
-    public boolean delete(final IDiagram d, Collection<IElement> ss) {\r
-        TimeLogger.resetTimeAndLog(getClass(), "delete");\r
-        \r
-        Topology topology = d.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
-        RelationshipHandler erh = d.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);\r
-\r
-        if (DEBUG_DELETE) {\r
-            System.out.println("diagram: " + d);\r
-            for (IElement e : d.getSnapshot()) {\r
-                ElementClass ec = e.getElementClass();\r
-                System.out.println("\t-element " + e);\r
-                System.out.println("\t  -class " + e.getElementClass());\r
-                if (ec.containsClass(ConnectionHandler.class)) {\r
-                    ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
-                    for (IElement child : ce.getBranchPoints(null)) {\r
-                        System.out.println("\t\t-branch " + child);\r
-                        System.out.println("\t\t  -class " + child.getElementClass());\r
-                    }\r
-                    for (IElement child : ce.getSegments(null)) {\r
-                        System.out.println("\t\t-segment " + child);\r
-                        System.out.println("\t\t  -class " + child.getElementClass());\r
-                    }\r
-                }\r
-            }\r
-            System.out.println("delete requested for elements:");\r
-            for (IElement e : ss)\r
-                System.out.println("\t-element " + e);\r
-        }\r
-\r
-        // Analyze removals:\r
-        //  - separate elements and connections\r
-        //  - find all connections attached to the elements and remove them too\r
-        Deque<IElement> elementsToProcess = new ArrayDeque<IElement>(ss);\r
-        Set<IElement> processedElements = new HashSet<IElement>();\r
-        Set<IElement> relationshipsProcessedForElement = new HashSet<IElement>();\r
-\r
-        final Collection<IElement> elements = new ArrayList<IElement>();\r
-        final Set<IElement> edges = new HashSet<IElement>();\r
-        Collection<Connection> connections = new ArrayList<Connection>();\r
-        Collection<Terminal> terminals = new ArrayList<Terminal>();\r
-        Collection<Relation> relations = new ArrayList<Relation>();\r
-        while (!elementsToProcess.isEmpty()) {\r
-            IElement el = elementsToProcess.pollFirst();\r
-\r
-            if (relationshipsProcessedForElement.add(el)) {\r
-                // Check for relationships to other elements and mark child\r
-                // elements to be removed before the parent element.\r
-                relations.clear();\r
-                erh.getRelations(d, el, relations);\r
-                if (!relations.isEmpty()) {\r
-                    boolean restart = false;\r
-                    for (Relation r : relations) {\r
-                        //System.out.println("FOUND RELATION: " + r);\r
-                        if (r.getRelationship() == Relationship.PARENT_OF) {\r
-                            if ((r.getObject() instanceof IElement)) {\r
-                                IElement ee = (IElement) r.getObject();\r
-                                if (d.containsElement(ee)) {\r
-                                    //System.out.println("DIAGRAM CONTAINS OBJECT: " + r.getObject());\r
-\r
-                                    // Mark the object also to be processed for removal.\r
-                                    elementsToProcess.addFirst(ee);\r
-                                    restart = true;\r
-                                }\r
-                            }\r
-                        }\r
-                    }\r
-                    if (restart) {\r
-                        // Only process this element after we're sure that\r
-                        // all its children have been processed.\r
-                        elementsToProcess.addLast(el);\r
-                        continue;\r
-                    }\r
-                }\r
-            }\r
-\r
-            if (!processedElements.add(el))\r
-                continue;\r
-\r
-            TerminalTopology tt = el.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);\r
-            BendsHandler bh = el.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-\r
-            if (bh != null) {\r
-                // Verify that the edge is NOT between two branch points.\r
-                // If it is, do not allow deletion because it is the only case\r
-                // which can break a connection tree into a connection forest.\r
-                // We do not want that to happen.\r
-                Connection begin = topology.getConnection(el, EdgeEnd.Begin);\r
-                Connection end = topology.getConnection(el, EdgeEnd.End);\r
-\r
-                // Try to work with cases where the model is somewhat corrupt.\r
-                if (begin != null && end != null) {\r
-                    if (PickFilter.FILTER_BRANCH_POINT.accept(begin.node) && PickFilter.FILTER_BRANCH_POINT.accept(end.node)) {\r
-                        error("Deletion of branch point connecting edges is not allowed. Must be connected to a node terminal.");\r
-                        return false;\r
-                    }\r
-                }\r
-\r
-                if (DEBUG_DELETE)\r
-                    System.out.println("ADDED EDGE FOR REMOVAL: " + el);\r
-                edges.add(el);\r
-            } else {\r
-                if (DEBUG_DELETE)\r
-                    System.out.println("ADDED ELEMENT FOR REMOVAL: " + el);\r
-                elements.add(el);\r
-\r
-                if (tt != null) {\r
-                    terminals.clear();\r
-                    tt.getTerminals(el, terminals);\r
-                    connections.clear();\r
-                    for (Terminal terminal : terminals)\r
-                        topology.getConnections(el, terminal, connections);\r
-                    for (Connection c : connections) {\r
-                        if (c.edge != null) {\r
-                            if (c.edge.getElementClass().containsClass(BendsHandler.class))\r
-                                edges.add(c.edge);\r
-                            if (DEBUG_DELETE)\r
-                                System.out.println("TERMINAL CONNECTION WILL BE DISCONNECTED: " + c);\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        if (elements.isEmpty() && edges.isEmpty())\r
-            return false;\r
-\r
-        if (DEBUG_DELETE) {\r
-            System.out.println("gathered elements to delete:");\r
-            System.out.println("\telements:");\r
-            if (!elements.isEmpty())\r
-                for (IElement e : elements)\r
-                    System.out.println("\t\t" + e);\r
-            System.out.println("\tedges:");\r
-            if (!edges.isEmpty())\r
-                for (IElement e : edges)\r
-                    System.out.println("\t\t" + e);\r
-        }\r
-\r
-        final IDiagram diagram = this.diagram;\r
-        final ISynchronizationContext syncContext = ElementFactoryUtil.getContextChecked(diagram); \r
-\r
-        new DatabaseJob("Delete selection") {\r
-            @Override\r
-            protected IStatus run(IProgressMonitor monitor) {\r
-                try {\r
-                    delete(monitor);\r
-                    return Status.OK_STATUS;\r
-                } catch (CannotRemoveException e) {\r
-                       ShowMessage.showInformation("Delete Selection Was Denied", e.getLocalizedMessage());\r
-                    return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, e.getLocalizedMessage(), e);\r
-                } catch (DatabaseException e) {\r
-                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unexpected error in delete.", e);\r
-                } finally {\r
-                    error(null);\r
-                    monitor.done();\r
-                }\r
-            }\r
-\r
-            private void delete(IProgressMonitor monitor) throws DatabaseException {\r
-                Simantics.getSession().syncRequest(new WriteRequest() {\r
-                    Set<Resource> connectionsToRemove = new HashSet<Resource>();\r
-                    Set<Resource> touchedConnections = new HashSet<Resource>();\r
-\r
-                    @Override\r
-                    public void perform(WriteGraph graph) throws DatabaseException {\r
-                        validateRemoval(graph);\r
-                        graph.markUndoPoint();\r
-\r
-                        ConnectionUtil cu = new ConnectionUtil(graph);\r
-\r
-                        // Remove edges\r
-                        for (IElement edge : edges) {\r
-                            ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
-                            touchConnection( ce.getConnection() );\r
-\r
-                            if (DEBUG_DELETE)\r
-                                System.out.println("REMOVING EDGE: " + edge);\r
-                            Object obj = ElementUtils.getObject(edge);\r
-                            if (obj instanceof EdgeResource) {\r
-                                cu.remove((EdgeResource) obj);\r
-                            }\r
-                        }\r
-\r
-                        // Remove elements\r
-                        for (IElement element : elements) {\r
-                            ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class);\r
-                            if (ch != null) {\r
-                                if (DEBUG_DELETE)\r
-                                    System.out.println("MARKING CONNECTION TO BE REMOVED: " + element);\r
-                                connectionsToRemove.add( (Resource) ElementUtils.getObject(element) );\r
-                            } else {\r
-                                ConnectionEntity ce = element.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
-                                if(ce != null) {\r
-                                    if (DEBUG_DELETE)\r
-                                        System.out.println("REMOVING BRANCH POINT: " + element);\r
-                                    new RemoveBranchpoint(element).perform(graph);\r
-                                    touchConnection( ce.getConnection() );\r
-                                } else {\r
-                                    if (DEBUG_DELETE)\r
-                                        System.out.println("REMOVING ELEMENT: " + element);\r
-\r
-                                    Object obj = ElementUtils.getObject(element);\r
-                                    if (obj instanceof Resource) {\r
-                                        // Get terminal connections for element\r
-                                        Collection<Resource> connectors = cu.getTerminalConnectors((Resource) obj, null);\r
-                                        for (Resource connector : connectors) {\r
-                                            Resource connection = ConnectionUtil.tryGetConnection(graph, connector);\r
-                                            if (connection != null)\r
-                                                touchConnection( connection );\r
-                                            cu.disconnectFromAllRouteNodes(connector);\r
-                                        }\r
-\r
-                                        new RemoveElement((Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), (Resource)element.getHint(ElementHints.KEY_OBJECT)).perform(graph);\r
-                                    }\r
-                                }\r
-                            }\r
-                        }\r
-\r
-                        // Check all touched connections to see if they are empty.\r
-                        for (Resource connection : touchedConnections) {\r
-                            int removedConnectors = cu.removeUnusedConnectors(connection);\r
-                            if (DEBUG_DELETE)\r
-                                System.out.println("PRUNED " + removedConnectors + " CONNECTORS FROM TOUCHED CONNECTION " + connection);\r
-                            while (true) {\r
-                                int removedInteriorRouteNodes = cu.removeExtraInteriorRouteNodes(connection);\r
-                                if (DEBUG_DELETE)\r
-                                    System.out.println("PRUNED " + removedInteriorRouteNodes + " INTERIOR ROUTE NODES FROM TOUCHED CONNECTION " + connection);\r
-                                if (removedInteriorRouteNodes == 0)\r
-                                    break;\r
-                            }\r
-                            int connectors = cu.getConnectedConnectors(connection, null).size();\r
-                            if (DEBUG_DELETE)\r
-                                System.out.println("\t" + connectors + " CONNECTORS LEFT");\r
-                            if (connectors < 2) {\r
-                                connectionsToRemove.add(connection);\r
-                            }\r
-                        }\r
-\r
-                        // Remove selected/left-over empty connections\r
-                        for (Resource connection : connectionsToRemove) {\r
-                            if (DEBUG_DELETE)\r
-                                System.out.println("REMOVING CONNECTION: " + connection);\r
-                            RemoveElement.removeElement(graph, (Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), connection);\r
-                        }\r
-                    }\r
-\r
-                    private void validateRemoval(ReadGraph graph) throws DatabaseException, CannotRemoveException {\r
-                        ArrayList<String> problems = new ArrayList<String>(elements.size());\r
-                        for (IElement element : elements) {\r
-                            Object obj = ElementUtils.getObject(element);\r
-                            if (obj instanceof Resource) {\r
-                                Remover remover = RemoverUtil.getPossibleRemover(graph, (Resource) obj);\r
-                                if (remover != null) {\r
-                                    String problem = remover.canRemove(graph, new HashMap<Object, Object>(4));\r
-                                    if (problem != null) {\r
-                                        problems.add(problem);\r
-                                    }\r
-                                }\r
-                            }\r
-                        }\r
-                        if (!problems.isEmpty()) {\r
-                            throw new CannotRemoveException(EString.implode(problems));\r
-                        }\r
-                    }\r
-\r
-                    void touchConnection(Object c) {\r
-                        if (DEBUG_DELETE)\r
-                            System.out.println("TOUCHED CONNECTION: " + c);\r
-                        if (c instanceof IElement) {\r
-                            Object obj = ElementUtils.getObject((IElement) c);\r
-                            if (obj instanceof Resource)\r
-                                touchedConnections.add((Resource) obj);\r
-                        } else if (c instanceof Resource) {\r
-                            touchedConnections.add((Resource) c);\r
-                        }\r
-                    }\r
-                });\r
-            }\r
-        }.schedule();\r
-\r
-        return true;\r
-    }\r
-\r
-    void message(final String message) {\r
-        if (statusLine == null)\r
-            return;\r
-        swtExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                statusLine.setMessage(message);\r
-                statusLine.setErrorMessage(null);\r
-            }\r
-        });\r
-    }\r
-\r
-    void error(final String message) {\r
-        if (statusLine == null)\r
-            return;\r
-        swtExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                statusLine.setErrorMessage(message);\r
-            }\r
-        });\r
-    }\r
-\r
-    void swtExec(Runnable r) {\r
-        ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r);\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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
+ *******************************************************************************/
+package org.simantics.diagram.handler;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.swt.widgets.Display;
+import org.simantics.DatabaseJob;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.Remover;
+import org.simantics.db.layer0.exception.CannotRemoveException;
+import org.simantics.db.layer0.util.RemoverUtil;
+import org.simantics.diagram.adapter.ElementFactoryUtil;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.content.EdgeResource;
+import org.simantics.diagram.internal.Activator;
+import org.simantics.diagram.synchronization.ISynchronizationContext;
+import org.simantics.diagram.synchronization.graph.RemoveBranchpoint;
+import org.simantics.diagram.synchronization.graph.RemoveElement;
+import org.simantics.diagram.ui.DiagramModelHints;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
+import org.simantics.g2d.connection.ConnectionEntity;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
+import org.simantics.g2d.diagram.handler.Relationship;
+import org.simantics.g2d.diagram.handler.RelationshipHandler;
+import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;
+import org.simantics.g2d.diagram.handler.Topology;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
+import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.element.ElementClass;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;
+import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.utils.logging.TimeLogger;
+import org.simantics.utils.strings.EString;
+import org.simantics.utils.threads.SWTThread;
+import org.simantics.utils.threads.ThreadUtils;
+import org.simantics.utils.ui.dialogs.ShowMessage;
+
+/**
+ * DeleteHandler is a canvas handler for Commands.DELETE commands for an
+ * IDiagram.
+ * 
+ * <p>
+ * The handler attempts to delete the current selection for pointer 0, meaning
+ * {@link Selection#SELECTION0}.
+ * </p>
+ * 
+ * <p>
+ * The handler logic goes as follows:
+ * </p>
+ * <ol>
+ * <li>Separate nodes and edges form the the removed selection</li>
+ * <li>Find all edges attached to the removed nodes and remove them too</li>
+ * <li>Delete connections that contain less than 2 terminal connections</li>
+ * </ol>
+ * 
+ * @see Selection for the current diagram selection source
+ * 
+ * @author Tuukka Lehtonen
+ * 
+ * TODO: start using WorkbenchStatusLine participant
+ */
+public class DeleteHandler extends AbstractDiagramParticipant {
+
+    public static final boolean DEBUG_DELETE = false;
+
+    @Dependency Selection sel;
+
+    private final IStatusLineManager statusLine;
+
+    public DeleteHandler(IStatusLineManager statusLine) {
+        this.statusLine = statusLine;
+    }
+
+    @EventHandler(priority = 0)
+    public boolean handleCommand(CommandEvent e) {
+        if (Commands.DELETE.equals( e.command )) {
+            IDiagram d = diagram;
+            if (d == null)
+                return true;
+
+            Set<IElement> ss = sel.getSelection(0);
+            if (ss.isEmpty())
+                return true;
+
+            if (delete(d, ss)) {
+                sel.clear(0);
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    public boolean delete(final IDiagram d, Collection<IElement> ss) {
+        TimeLogger.resetTimeAndLog(getClass(), "delete");
+        
+        Topology topology = d.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
+        RelationshipHandler erh = d.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);
+
+        if (DEBUG_DELETE) {
+            System.out.println("diagram: " + d);
+            for (IElement e : d.getSnapshot()) {
+                ElementClass ec = e.getElementClass();
+                System.out.println("\t-element " + e);
+                System.out.println("\t  -class " + e.getElementClass());
+                if (ec.containsClass(ConnectionHandler.class)) {
+                    ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+                    for (IElement child : ce.getBranchPoints(null)) {
+                        System.out.println("\t\t-branch " + child);
+                        System.out.println("\t\t  -class " + child.getElementClass());
+                    }
+                    for (IElement child : ce.getSegments(null)) {
+                        System.out.println("\t\t-segment " + child);
+                        System.out.println("\t\t  -class " + child.getElementClass());
+                    }
+                }
+            }
+            System.out.println("delete requested for elements:");
+            for (IElement e : ss)
+                System.out.println("\t-element " + e);
+        }
+
+        // Analyze removals:
+        //  - separate elements and connections
+        //  - find all connections attached to the elements and remove them too
+        Deque<IElement> elementsToProcess = new ArrayDeque<IElement>(ss);
+        Set<IElement> processedElements = new HashSet<IElement>();
+        Set<IElement> relationshipsProcessedForElement = new HashSet<IElement>();
+
+        final Collection<IElement> elements = new ArrayList<IElement>();
+        final Set<IElement> edges = new HashSet<IElement>();
+        Collection<Connection> connections = new ArrayList<Connection>();
+        Collection<Terminal> terminals = new ArrayList<Terminal>();
+        Collection<Relation> relations = new ArrayList<Relation>();
+        while (!elementsToProcess.isEmpty()) {
+            IElement el = elementsToProcess.pollFirst();
+
+            if (relationshipsProcessedForElement.add(el)) {
+                // Check for relationships to other elements and mark child
+                // elements to be removed before the parent element.
+                relations.clear();
+                erh.getRelations(d, el, relations);
+                if (!relations.isEmpty()) {
+                    boolean restart = false;
+                    for (Relation r : relations) {
+                        //System.out.println("FOUND RELATION: " + r);
+                        if (r.getRelationship() == Relationship.PARENT_OF) {
+                            if ((r.getObject() instanceof IElement)) {
+                                IElement ee = (IElement) r.getObject();
+                                if (d.containsElement(ee)) {
+                                    //System.out.println("DIAGRAM CONTAINS OBJECT: " + r.getObject());
+
+                                    // Mark the object also to be processed for removal.
+                                    elementsToProcess.addFirst(ee);
+                                    restart = true;
+                                }
+                            }
+                        }
+                    }
+                    if (restart) {
+                        // Only process this element after we're sure that
+                        // all its children have been processed.
+                        elementsToProcess.addLast(el);
+                        continue;
+                    }
+                }
+            }
+
+            if (!processedElements.add(el))
+                continue;
+
+            TerminalTopology tt = el.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
+            BendsHandler bh = el.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+
+            if (bh != null) {
+                // Verify that the edge is NOT between two branch points.
+                // If it is, do not allow deletion because it is the only case
+                // which can break a connection tree into a connection forest.
+                // We do not want that to happen.
+                Connection begin = topology.getConnection(el, EdgeEnd.Begin);
+                Connection end = topology.getConnection(el, EdgeEnd.End);
+
+                // Try to work with cases where the model is somewhat corrupt.
+                if (begin != null && end != null) {
+                    if (PickFilter.FILTER_BRANCH_POINT.accept(begin.node) && PickFilter.FILTER_BRANCH_POINT.accept(end.node)) {
+                        error("Deletion of branch point connecting edges is not allowed. Must be connected to a node terminal.");
+                        return false;
+                    }
+                }
+
+                if (DEBUG_DELETE)
+                    System.out.println("ADDED EDGE FOR REMOVAL: " + el);
+                edges.add(el);
+            } else {
+                if (DEBUG_DELETE)
+                    System.out.println("ADDED ELEMENT FOR REMOVAL: " + el);
+                elements.add(el);
+
+                if (tt != null) {
+                    terminals.clear();
+                    tt.getTerminals(el, terminals);
+                    connections.clear();
+                    for (Terminal terminal : terminals)
+                        topology.getConnections(el, terminal, connections);
+                    for (Connection c : connections) {
+                        if (c.edge != null) {
+                            if (c.edge.getElementClass().containsClass(BendsHandler.class))
+                                edges.add(c.edge);
+                            if (DEBUG_DELETE)
+                                System.out.println("TERMINAL CONNECTION WILL BE DISCONNECTED: " + c);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (elements.isEmpty() && edges.isEmpty())
+            return false;
+
+        if (DEBUG_DELETE) {
+            System.out.println("gathered elements to delete:");
+            System.out.println("\telements:");
+            if (!elements.isEmpty())
+                for (IElement e : elements)
+                    System.out.println("\t\t" + e);
+            System.out.println("\tedges:");
+            if (!edges.isEmpty())
+                for (IElement e : edges)
+                    System.out.println("\t\t" + e);
+        }
+
+        final IDiagram diagram = this.diagram;
+        final ISynchronizationContext syncContext = ElementFactoryUtil.getContextChecked(diagram); 
+
+        new DatabaseJob("Delete selection") {
+            @Override
+            protected IStatus run(IProgressMonitor monitor) {
+                try {
+                    delete(monitor);
+                    return Status.OK_STATUS;
+                } catch (CannotRemoveException e) {
+                       ShowMessage.showInformation("Delete Selection Was Denied", e.getLocalizedMessage());
+                    return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, e.getLocalizedMessage(), e);
+                } catch (DatabaseException e) {
+                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unexpected error in delete.", e);
+                } finally {
+                    error(null);
+                    monitor.done();
+                }
+            }
+
+            private void delete(IProgressMonitor monitor) throws DatabaseException {
+                SubMonitor subMonitor = SubMonitor.convert(monitor, 4);
+                Simantics.getSession().syncRequest(new WriteRequest() {
+                    Set<Resource> connectionsToRemove = new HashSet<Resource>();
+                    Set<Resource> touchedConnections = new HashSet<Resource>();
+
+                    @Override
+                    public void perform(WriteGraph graph) throws DatabaseException {
+                        validateRemoval(graph);
+                        graph.markUndoPoint();
+
+                        ConnectionUtil cu = new ConnectionUtil(graph);
+                        SubMonitor edgeElementMonitor = subMonitor.split(2); // Consume 50 % of the monitor
+                        edgeElementMonitor.setWorkRemaining(edges.size() + elements.size()); // set work remaining to size of edges and elements
+                        edgeElementMonitor.setTaskName("Removing edges");
+                        // Remove edges
+                        for (IElement edge : edges) {
+                            ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+                            touchConnection( ce.getConnection() );
+
+                            if (DEBUG_DELETE)
+                                System.out.println("REMOVING EDGE: " + edge);
+                            Object obj = ElementUtils.getObject(edge);
+                            if (obj instanceof EdgeResource) {
+                                cu.remove((EdgeResource) obj);
+                            }
+                            edgeElementMonitor.worked(1);
+                        }
+                        edgeElementMonitor.setTaskName("Removing elements");
+                        // Remove elements
+                        for (IElement element : elements) {
+                            ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class);
+                            if (ch != null) {
+                                if (DEBUG_DELETE)
+                                    System.out.println("MARKING CONNECTION TO BE REMOVED: " + element);
+                                connectionsToRemove.add( (Resource) ElementUtils.getObject(element) );
+                            } else {
+                                ConnectionEntity ce = element.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+                                if(ce != null) {
+                                    if (DEBUG_DELETE)
+                                        System.out.println("REMOVING BRANCH POINT: " + element);
+                                    new RemoveBranchpoint(element).perform(graph);
+                                    touchConnection( ce.getConnection() );
+                                } else {
+                                    if (DEBUG_DELETE)
+                                        System.out.println("REMOVING ELEMENT: " + element);
+
+                                    Object obj = ElementUtils.getObject(element);
+                                    if (obj instanceof Resource) {
+                                        // Get terminal connections for element
+                                        Collection<Resource> connectors = cu.getTerminalConnectors((Resource) obj, null);
+                                        for (Resource connector : connectors) {
+                                            Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
+                                            if (connection != null)
+                                                touchConnection( connection );
+                                            cu.disconnectFromAllRouteNodes(connector);
+                                        }
+
+                                        new RemoveElement((Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), (Resource)element.getHint(ElementHints.KEY_OBJECT)).perform(graph);
+                                    }
+                                }
+                            }
+                            edgeElementMonitor.worked(1);
+                        }
+                        SubMonitor connectionsMonitor = subMonitor.split(1); // Consume 50 % of the monitor
+                        connectionsMonitor.setWorkRemaining(touchedConnections.size()); // set work remaining to size of edges and elements
+                        connectionsMonitor.setTaskName("Pruning connectors");
+                        // Check all touched connections to see if they are empty.
+                        for (Resource connection : touchedConnections) {
+                            int removedConnectors = cu.removeUnusedConnectors(connection);
+                            if (DEBUG_DELETE)
+                                System.out.println("PRUNED " + removedConnectors + " CONNECTORS FROM TOUCHED CONNECTION " + connection);
+                            while (true) {
+                                int removedInteriorRouteNodes = cu.removeExtraInteriorRouteNodes(connection);
+                                if (DEBUG_DELETE)
+                                    System.out.println("PRUNED " + removedInteriorRouteNodes + " INTERIOR ROUTE NODES FROM TOUCHED CONNECTION " + connection);
+                                if (removedInteriorRouteNodes == 0)
+                                    break;
+                            }
+                            int connectors = cu.getConnectedConnectors(connection, null).size();
+                            if (DEBUG_DELETE)
+                                System.out.println("\t" + connectors + " CONNECTORS LEFT");
+                            if (connectors < 2) {
+                                connectionsToRemove.add(connection);
+                            }
+                            connectionsMonitor.worked(1);
+                        }
+                        SubMonitor connectionsMonitor2 = subMonitor.split(1); // Consume 50 % of the monitor
+                        connectionsMonitor2.setWorkRemaining(connectionsToRemove.size()); // set work remaini
+                        connectionsMonitor2.setTaskName("Removing leftover connections");
+                        // Remove selected/left-over empty connections
+                        for (Resource connection : connectionsToRemove) {
+                            if (DEBUG_DELETE)
+                                System.out.println("REMOVING CONNECTION: " + connection);
+                            RemoveElement.removeElement(graph, (Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), connection);
+                            connectionsMonitor2.worked(1);
+                        }
+                    }
+
+                    private void validateRemoval(ReadGraph graph) throws DatabaseException, CannotRemoveException {
+                        ArrayList<String> problems = new ArrayList<String>(elements.size());
+                        for (IElement element : elements) {
+                            Object obj = ElementUtils.getObject(element);
+                            if (obj instanceof Resource) {
+                                Remover remover = RemoverUtil.getPossibleRemover(graph, (Resource) obj);
+                                if (remover != null) {
+                                    String problem = remover.canRemove(graph, new HashMap<Object, Object>(4));
+                                    if (problem != null) {
+                                        problems.add(problem);
+                                    }
+                                }
+                            }
+                        }
+                        if (!problems.isEmpty()) {
+                            throw new CannotRemoveException(EString.implode(problems));
+                        }
+                    }
+
+                    void touchConnection(Object c) {
+                        if (DEBUG_DELETE)
+                            System.out.println("TOUCHED CONNECTION: " + c);
+                        if (c instanceof IElement) {
+                            Object obj = ElementUtils.getObject((IElement) c);
+                            if (obj instanceof Resource)
+                                touchedConnections.add((Resource) obj);
+                        } else if (c instanceof Resource) {
+                            touchedConnections.add((Resource) c);
+                        }
+                    }
+                });
+            }
+        }.schedule();
+
+        return true;
+    }
+
+    void message(final String message) {
+        if (statusLine == null)
+            return;
+        swtExec(new Runnable() {
+            @Override
+            public void run() {
+                statusLine.setMessage(message);
+                statusLine.setErrorMessage(null);
+            }
+        });
+    }
+
+    void error(final String message) {
+        if (statusLine == null)
+            return;
+        swtExec(new Runnable() {
+            @Override
+            public void run() {
+                statusLine.setErrorMessage(message);
+            }
+        });
+    }
+
+    void swtExec(Runnable r) {
+        ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r);
+    }
+
+}