]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/GraphToDiagramSynchronizer.java
Even more fixes to layers
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / adapter / GraphToDiagramSynchronizer.java
index dd6c9b7d10f7e7e916264a30bc3f24af71180627..a1fa5bee59dd6d609c6d2eaf1846e82346c3406c 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * Copyright (c) 2007, 2018 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
@@ -11,9 +11,6 @@
  *******************************************************************************/
 package org.simantics.diagram.adapter;
 
-import gnu.trove.map.hash.TObjectIntHashMap;
-import gnu.trove.set.hash.THashSet;
-
 import java.awt.geom.AffineTransform;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayDeque;
@@ -49,6 +46,7 @@ import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;
 import org.simantics.db.common.procedure.adapter.CacheListener;
 import org.simantics.db.common.procedure.adapter.ListenerSupport;
 import org.simantics.db.common.procedure.adapter.ProcedureAdapter;
+import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
 import org.simantics.db.common.request.AsyncReadRequest;
 import org.simantics.db.common.request.ReadRequest;
 import org.simantics.db.common.session.SessionEventListenerAdapter;
@@ -129,7 +127,6 @@ import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.element.IElementClassProvider;
 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
-import org.simantics.g2d.element.handler.ElementHandler;
 import org.simantics.g2d.element.handler.ElementLayerListener;
 import org.simantics.g2d.element.handler.TerminalTopology;
 import org.simantics.g2d.element.impl.Element;
@@ -140,6 +137,7 @@ import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.profile.DataNodeConstants;
 import org.simantics.scenegraph.profile.DataNodeMap;
 import org.simantics.scenegraph.profile.common.ProfileObserver;
+import org.simantics.scl.runtime.tuple.Tuple3;
 import org.simantics.structural2.modelingRules.IModelingRules;
 import org.simantics.utils.datastructures.ArrayMap;
 import org.simantics.utils.datastructures.MapSet;
@@ -157,6 +155,11 @@ import org.simantics.utils.strings.EString;
 import org.simantics.utils.threads.ThreadUtils;
 import org.simantics.utils.threads.logger.ITask;
 import org.simantics.utils.threads.logger.ThreadLogger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.map.hash.TObjectIntHashMap;
+import gnu.trove.set.hash.THashSet;
 
 /**
  * This class loads a diagram contained in the graph database into the runtime
@@ -249,6 +252,8 @@ import org.simantics.utils.threads.logger.ThreadLogger;
  */
 public class GraphToDiagramSynchronizer extends AbstractDisposable implements IDiagramLoader, IModifiableSynchronizationContext {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(GraphToDiagramSynchronizer.class);
+
     /**
      * Controls whether the class adds hint listeners to each diagram element
      * that try to perform basic sanity checks on changes happening in element
@@ -611,6 +616,17 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
      */
     ConcurrentMap<Object, ConnectionEntityImpl> dataConnection = new ConcurrentHashMap<Object, ConnectionEntityImpl>();
 
+
+    void mapElementIfNew(final Object data, final IElement element) {
+        IElement mapped = getMappedElement(data);
+        if(mapped == null) {
+            mapElement(data, element);
+            currentUpdater.addedElements.add(element);
+            currentUpdater.addedElementMap.put(data, element);
+        }
+    }
+
+    
     /**
      * @param data
      * @param element
@@ -748,21 +764,8 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
         public ElementClass substitute(IDiagram d, ElementClass ec) {
             if (d != diagram)
                 throw new IllegalArgumentException("specified diagram does not have this SubstituteElementClass handler");
-
-            // If the element class is our own, there's no point in creating
-            // a copy of it.
-            if (ec.contains(elementLayerListener))
-                return ec;
-
-            List<ElementHandler> all = ec.getAll();
-            List<ElementHandler> result = new ArrayList<ElementHandler>(all.size());
-            for (ElementHandler eh : all) {
-                if (eh instanceof ElementLayerListenerImpl)
-                    result.add(elementLayerListener);
-                else
-                    result.add(eh);
-            }
-            return ElementClass.compile(result).setId(ec.getId());
+            // Nothing to substitute here
+            return ec;
         }
     }
 
@@ -1110,7 +1113,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             }
         } catch (InterruptedException e) {
             // Shouldn't happen.
-            e.printStackTrace();
+            LOGGER.error("Dispose interrupted!", e);
         } finally {
             detachSessionListener();
 
@@ -1156,7 +1159,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
      * A common handler for all elements that is used to listen to changes in
      * element visibility and focusability on diagram layers.
      */
-    class ElementLayerListenerImpl implements ElementLayerListener {
+    public class ElementLayerListenerImpl implements ElementLayerListener {
         private static final long serialVersionUID = -3410052116598828129L;
 
         @Override
@@ -1183,6 +1186,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             }
         }
 
+        @Override
+        public void flush() {
+            modificationQueue.flush();
+        }
+
         void changeTag(IElement e, Resource tag, boolean set) {
             Object object = e.getHint(ElementHints.KEY_OBJECT);
             Resource tagged = null;
@@ -1197,7 +1205,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             if (tagged == null)
                 return;
 
-            modificationQueue.async(new TagChange(tagged, tag, set), null);
+            modificationQueue.offer(new TagChange(tagged, tag, set), null);
         }
     };
 
@@ -1264,6 +1272,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
 
                         d.setHint(DiagramHints.KEY_LAYERS, layers);
                         d.setHint(DiagramHints.KEY_LAYERS_EDITOR, layers);
+                        d.setHint(DiagramHints.KEY_ELEMENT_LAYER_LISTENER, elementLayerListener);
 
                         d.addCompositionVetoListener(diagramListener);
                         d.addCompositionListener(diagramListener);
@@ -1301,7 +1310,8 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     task4.finish();
                     // ITask task5 = ThreadLogger.getInstance().begin("DiagramContentRequest2");
                     ITask task42 = ThreadLogger.getInstance().begin("DiagramContentRequest2");
-                    DiagramContents contents = g.syncRequest(query);
+                    DiagramContents contents = g.syncRequest(query, TransientCacheAsyncListener.instance());
+                    //System.err.println("contents: " + contents);
                     task42.finish();
                     // task5.finish();
                     monitor.worked(10);
@@ -1338,7 +1348,16 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                             ArrayMap.keys(ProfileKeys.DIAGRAM, ProfileKeys.CANVAS, ProfileKeys.NODE_MAP).values(GraphToDiagramSynchronizer.this.diagram, canvas, dn),
                             new CanvasNotification(canvas));
 
-                    profileObserver.listen(g, GraphToDiagramSynchronizer.this);
+                    g.getSession().asyncRequest(new AsyncReadRequest() {
+                        @Override
+                        public void run(AsyncReadGraph graph) {
+                          ProfileObserver po = profileObserver;
+                            if (po != null)
+                                po.listen(graph, GraphToDiagramSynchronizer.this);
+                            else
+                                LOGGER.info("profileObserver has been disposed already!");
+                        }
+                    });
 
                     return d;
 
@@ -1603,11 +1622,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
         }
 
         public void clear() {
-               // Prevent DiagramContents leakage through DisposableListeners.
-               lastContent = null;
-               content = null;
-               changes = null;
-               
+            // Prevent DiagramContents leakage through DisposableListeners.
+            lastContent = null;
+            content = null;
+            changes = null;
+
             this.addedElements.clear();
             this.removedElements.clear();
             this.addedConnectionSegments.clear();
@@ -1623,7 +1642,91 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             this.removedRouteGraphConnections.clear();
         }
 
-        void processNodes(AsyncReadGraph graph) {
+        class LoadNodeListener extends DisposableListener<IElement> {
+
+            final Resource element;
+            public IElement lastLoaded;
+
+            public LoadNodeListener(ListenerSupport support, Resource element) {
+                super(support);
+                this.element = element;
+            }
+
+            @Override
+            public String toString() {
+                return "Node load listener for " + element;
+            }
+
+            public void applyFirst(IElement loaded) {
+
+                Object data = loaded.getHint(ElementHints.KEY_OBJECT);
+
+                if (addedElementMap.containsKey(data)) {
+                    // This element was just loaded, in
+                    // which case its hints need to
+                    // uploaded to the real mapped
+                    // element immediately.
+                    IElement mappedElement = getMappedElement(data);
+                    if (DebugPolicy.DEBUG_NODE_LISTENER)
+                        System.out.println("LOADED ADDED ELEMENT, currently mapped element: " + mappedElement);
+                    if (mappedElement != null && (mappedElement instanceof Element)) {
+                        if (DebugPolicy.DEBUG_NODE_LISTENER) {
+                            System.out.println("  mapped hints: " + mappedElement.getHints());
+                            System.out.println("  loaded hints: " + loaded.getHints());
+                        }
+                        updateMappedElement((Element) mappedElement, loaded);
+                    }
+                }
+
+            }
+
+            @Override
+            public void execute(IElement loaded) {
+
+                // Invoked when the element has been loaded.
+                if (DebugPolicy.DEBUG_NODE_LISTENER)
+                    System.out.println("NODE LoadListener for " + loaded);
+
+                if (loaded == null) {
+                    disposeListener();
+                    return;
+                }
+
+
+                boolean first = lastLoaded == null;
+
+                lastLoaded = loaded;
+
+                /*
+                 * The first invocation is postponed
+                 */
+                if(first) {
+                    applyFirst(loaded);
+                    return;
+                }
+
+                Object data = loaded.getHint(ElementHints.KEY_OBJECT);
+
+                // Logic for disposing listener
+                if (!previousContent.nodeSet.contains(data)) {
+                    if (DebugPolicy.DEBUG_NODE_LISTENER)
+                        System.out.println("NODE LoadListener, node not in current content: " + data + ". Disposing.");
+                    disposeListener();
+                    return;
+                }
+
+                // This element was already loaded.
+                // Just schedule an update some time
+                // in the future.
+                if (DebugPolicy.DEBUG_NODE_LISTENER)
+                    System.out.println("PREVIOUSLY LOADED NODE UPDATED, scheduling update into the future");
+                offerGraphUpdate( nodeUpdater(element, loaded) );
+
+            }
+
+        }
+
+        void processNodes(AsyncReadGraph graph) throws DatabaseException {
 
             for (Map.Entry<Resource, Change> entry : changes.elements.entrySet()) {
 
@@ -1635,7 +1738,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         IElement mappedElement = getMappedElement(element);
                         if (mappedElement == null) {
                             if (DebugPolicy.DEBUG_NODE_LOAD)
-                                graph.asyncRequest(new ReadRequest() {
+                                graph.syncRequest(new ReadRequest() {
                                     @Override
                                     public void run(ReadGraph graph) throws DatabaseException {
                                         System.out.println("    EXTERNALLY ADDED ELEMENT: "
@@ -1648,10 +1751,13 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
 
                                 // TODO: Connection loading has no listening, changes :Connection will not be noticed by this code!
                                 Listener<IElement> loadListener = new DisposableListener<IElement>(canvasListenerSupport) {
-                                       @Override
-                                       public String toString() {
-                                               return "Connection load listener for " + element;
-                                       }
+
+                                    boolean firstTime = true;
+
+                                    @Override
+                                    public String toString() {
+                                        return "Connection load listener for " + element;
+                                    }
                                     @Override
                                     public void execute(IElement loaded) {
                                         // Invoked when the element has been loaded.
@@ -1663,6 +1769,19 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                             return;
                                         }
 
+                                        if (firstTime) {
+
+                                            mapElement(element, loaded);
+                                            synchronized (GraphToDiagramUpdater.this) {
+                                                addedElements.add(loaded);
+                                                addedElementMap.put(element, loaded);
+                                                addedConnectionMap.put(element, loaded);
+                                            }
+
+                                            firstTime = false;
+
+                                        }
+
                                         Object data = loaded.getHint(ElementHints.KEY_OBJECT);
 
                                         // Logic for disposing listener
@@ -1702,10 +1821,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                 graph.asyncRequest(new ConnectionRequest(canvas, diagram, element, errorHandler, loadListener), new AsyncProcedure<IElement>() {
                                     @Override
                                     public void execute(AsyncReadGraph graph, final IElement e) {
-                                        if (e == null)
-                                            return;
 
-                                        //System.out.println("ConnectionRequestProcedure " + e);
                                         mapElement(element, e);
                                         synchronized (GraphToDiagramUpdater.this) {
                                             addedElements.add(e);
@@ -1723,13 +1839,20 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                             @Override
                                             public void execute(Resource connectionType) {
                                                 synchronized (GraphToDiagramUpdater.this) {
+                                                    IElement mapped = getMappedElement(element);
+                                                    assert(mapped != null);
+                                                    
+                                                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                                                        System.out.println("CONNECTION ENTITY CREATED " + e + " " + element);
+
                                                     //System.out.println("new connection entity " + e);
-                                                    ConnectionEntityImpl entity = new ConnectionEntityImpl(element, connectionType, e);
-                                                    e.setHint(ElementHints.KEY_CONNECTION_ENTITY, entity);
+                                                    ConnectionEntityImpl entity = new ConnectionEntityImpl(element, connectionType, mapped);
+                                                    mapped.setHint(ElementHints.KEY_CONNECTION_ENTITY, entity);
                                                     addedConnectionEntities.put(element, entity);
                                                 }
                                             }
                                         });
+
                                     }
 
                                     @Override
@@ -1739,65 +1862,19 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                 });
                             } else if (content.nodeSet.contains(element)) {
 
-                                Listener<IElement> loadListener = new DisposableListener<IElement>(canvasListenerSupport) {
-                                       @Override
-                                       public String toString() {
-                                               return "Node load listener for " + element;
-                                       }
-                                    @Override
-                                    public void execute(IElement loaded) {
-                                        // Invoked when the element has been loaded.
-                                        if (DebugPolicy.DEBUG_NODE_LISTENER)
-                                            System.out.println("NODE LoadListener for " + loaded);
-
-                                        if (loaded == null) {
-                                            disposeListener();
-                                            return;
-                                        }
-
-                                        Object data = loaded.getHint(ElementHints.KEY_OBJECT);
-
-                                        // Logic for disposing listener
-                                        if (!previousContent.nodeSet.contains(data)) {
-                                            if (DebugPolicy.DEBUG_NODE_LISTENER)
-                                                System.out.println("NODE LoadListener, node not in current content: " + data + ". Disposing.");
-                                            disposeListener();
-                                            return;
-                                        }
-
-                                        if (addedElementMap.containsKey(data)) {
-                                            // This element was just loaded, in
-                                            // which case its hints need to
-                                            // uploaded to the real mapped
-                                            // element immediately.
-                                            IElement mappedElement = getMappedElement(data);
-                                            if (DebugPolicy.DEBUG_NODE_LISTENER)
-                                                System.out.println("LOADED ADDED ELEMENT, currently mapped element: " + mappedElement);
-                                            if (mappedElement != null && (mappedElement instanceof Element)) {
-                                                if (DebugPolicy.DEBUG_NODE_LISTENER) {
-                                                    System.out.println("  mapped hints: " + mappedElement.getHints());
-                                                    System.out.println("  loaded hints: " + loaded.getHints());
-                                                }
-                                                updateMappedElement((Element) mappedElement, loaded);
-                                            }
-                                        } else {
-                                            // This element was already loaded.
-                                            // Just schedule an update some time
-                                            // in the future.
-                                            if (DebugPolicy.DEBUG_NODE_LISTENER)
-                                                System.out.println("PREVIOUSLY LOADED NODE UPDATED, scheduling update into the future");
-                                            offerGraphUpdate( nodeUpdater(element, loaded) );
-                                        }
-                                    }
-                                };
+                                graph.asyncRequest(new ReadRequest() {
 
-                                //System.out.println("NODE REQUEST: " + element);
-                                graph.asyncRequest(new NodeRequest(canvas, diagram, element, loadListener), new AsyncProcedure<IElement>() {
                                     @Override
-                                    public void execute(AsyncReadGraph graph, IElement e) {
+                                    public void run(ReadGraph graph) throws DatabaseException {
+
+                                        LoadNodeListener loadListener = new LoadNodeListener(canvasListenerSupport, element);
+                                        Tuple3 t = graph.syncRequest(new NodeRequest2(canvas, diagram, element));
+                                        IElement e = (IElement)t.c0;
+                                        ElementClass ec = (ElementClass)t.c1;
+                                        org.simantics.diagram.adapter.ElementFactory ef = (org.simantics.diagram.adapter.ElementFactory)t.c2;
                                         if (e == null)
                                             return;
-
+                                        
                                         // This is invoked before the element is actually loaded.
                                         //System.out.println("NodeRequestProcedure " + e);
                                         if (DebugPolicy.DEBUG_NODE_LOAD)
@@ -1807,12 +1884,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                             addedElements.add(e);
                                             addedElementMap.put(element, e);
                                         }
-                                    }
 
-                                    @Override
-                                    public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                        error(throwable);
+                                        graph.syncRequest(new LoadRequest(canvas, diagram, ef, ec, element), loadListener);
+                                        
                                     }
+                                    
                                 });
 
                             } else {
@@ -1826,7 +1902,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     case REMOVED: {
                         IElement e = getMappedElement(element);
                         if (DebugPolicy.DEBUG_NODE_LOAD)
-                            graph.asyncRequest(new ReadRequest() {
+                            graph.syncRequest(new ReadRequest() {
                                 @Override
                                 public void run(ReadGraph graph) throws DatabaseException {
                                     System.out.println("    EXTERNALLY REMOVED ELEMENT: "
@@ -1839,6 +1915,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         }
                         break;
                     }
+                    default:
                 }
             }
         }
@@ -1872,6 +1949,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         }
                         break;
                     }
+                    default:
                 }
             }
         }
@@ -1915,6 +1993,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     case REMOVED: {
                         removedConnectionEntities.add(ce);
                     }
+                    default:
                 }
             }
 
@@ -1931,8 +2010,121 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                 }
             }
         }
+        
+        class LoadRouteGraphConnectionListener extends DisposableListener<IElement> {
+            
+            final Resource connection;
+            public IElement lastLoaded;
+
+            public LoadRouteGraphConnectionListener(ListenerSupport support, Resource connection) {
+                super(support);
+                this.connection = connection;
+            }
+
+            @Override
+            public String toString() {
+                return "processRouteGraphConnections " + connection;
+            }
+            
+            public void applyFirst(IElement loaded) {
+                
+                Object data = loaded.getHint(ElementHints.KEY_OBJECT);
+                if (addedElementMap.containsKey(data)) {
+                    // This element was just loaded, in
+                    // which case its hints need to
+                    // uploaded to the real mapped
+                    // element immediately.
+                    IElement mappedElement = getMappedElement(data);
+                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                        System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement);
+                    if (mappedElement instanceof Element) {
+                        if (DebugPolicy.DEBUG_CONNECTION_LISTENER) {
+                            System.out.println("  mapped hints: " + mappedElement.getHints());
+                            System.out.println("  loaded hints: " + loaded.getHints());
+                        }
+                        updateMappedElement((Element) mappedElement, loaded);
+                    }
+                }
+                
+            }
+            
+            @Override
+            public void execute(IElement loaded) {
+                
+                // Invoked when the element has been loaded.
+                if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                    System.out.println("ROUTE GRAPH CONNECTION LoadListener for " + loaded);
+
+                if (loaded == null) {
+                    disposeListener();
+                    return;
+                }
+
+                boolean first = lastLoaded == null;
+
+                lastLoaded = loaded;
+                
+                /*
+                 * The first invocation is postponed
+                 */
+                if(first) {
+                    applyFirst(loaded);
+                    return;
+                }
+
+                Object data = loaded.getHint(ElementHints.KEY_OBJECT);
+
+                // Logic for disposing listener
+                if (!previousContent.routeGraphConnectionSet.contains(data)) {
+                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                        System.out.println("ROUTE GRAPH CONNECTION LoadListener, connection not in current content: " + data + ". Disposing.");
+                    disposeListener();
+                    return;
+                }
+
+                if (addedElementMap.containsKey(data)) {
+                    // This element was just loaded, in
+                    // which case its hints need to
+                    // uploaded to the real mapped
+                    // element immediately.
+                    IElement mappedElement = getMappedElement(data);
+                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                        System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement);
+                    if (mappedElement instanceof Element) {
+                        if (DebugPolicy.DEBUG_CONNECTION_LISTENER) {
+                            System.out.println("  mapped hints: " + mappedElement.getHints());
+                            System.out.println("  loaded hints: " + loaded.getHints());
+                        }
+                        updateMappedElement((Element) mappedElement, loaded);
+                    }
+                } else {
+                    // This element was already loaded.
+                    // Just schedule an update some time
+                    // in the future.
+                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                        System.out.println("PREVIOUSLY LOADED ROUTE GRAPH CONNECTION UPDATED, scheduling update into the future: " + connection);
+
+                    Set<Object> dirtyNodes = new THashSet<Object>(4);
+                    IElement mappedElement = getMappedElement(connection);
+                    ConnectionEntity ce = mappedElement.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+                    if (ce != null) {
+                        for (Connection conn : ce.getTerminalConnections(null)) {
+                            Object o = conn.node.getHint(ElementHints.KEY_OBJECT);
+                            if (o != null) {
+                                dirtyNodes.add(o);
+                                if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
+                                    System.out.println("Marked connectivity dirty for node: " + conn.node);
+                            }
+                        }
+                    }
+
+                    offerGraphUpdate( routeGraphConnectionUpdater(connection, loaded, dirtyNodes) );
+                }
+            }
+        };
+
 
-        void processRouteGraphConnections(AsyncReadGraph graph) {
+        void processRouteGraphConnections(AsyncReadGraph graph) throws DatabaseException {
             for (Map.Entry<Resource, Change> entry : changes.routeGraphConnections.entrySet()) {
                 final Resource connection = entry.getKey();
 
@@ -1943,76 +2135,19 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         if (mappedElement != null)
                             continue;
 
-                        Listener<IElement> loadListener = new DisposableListener<IElement>(canvasListenerSupport) {
-                               @Override
-                               public String toString() {
-                                       return "processRouteGraphConnections " + connection;
-                               }
-                            @Override
-                            public void execute(IElement loaded) {
-                                // Invoked when the element has been loaded.
-                                if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
-                                    System.out.println("ROUTE GRAPH CONNECTION LoadListener for " + loaded);
+                        
+                        graph.asyncRequest(new ReadRequest() {
 
-                                if (loaded == null) {
-                                    disposeListener();
-                                    return;
-                                }
-
-                                Object data = loaded.getHint(ElementHints.KEY_OBJECT);
-
-                                // Logic for disposing listener
-                                if (!previousContent.routeGraphConnectionSet.contains(data)) {
-                                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
-                                        System.out.println("ROUTE GRAPH CONNECTION LoadListener, connection not in current content: " + data + ". Disposing.");
-                                    disposeListener();
-                                    return;
-                                }
+                            @Override
+                            public void run(ReadGraph graph) throws DatabaseException {
 
-                                if (addedElementMap.containsKey(data)) {
-                                    // This element was just loaded, in
-                                    // which case its hints need to
-                                    // uploaded to the real mapped
-                                    // element immediately.
-                                    IElement mappedElement = getMappedElement(data);
-                                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
-                                        System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement);
-                                    if (mappedElement instanceof Element) {
-                                        if (DebugPolicy.DEBUG_CONNECTION_LISTENER) {
-                                            System.out.println("  mapped hints: " + mappedElement.getHints());
-                                            System.out.println("  loaded hints: " + loaded.getHints());
-                                        }
-                                        updateMappedElement((Element) mappedElement, loaded);
-                                    }
-                                } else {
-                                    // This element was already loaded.
-                                    // Just schedule an update some time
-                                    // in the future.
-                                    if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
-                                        System.out.println("PREVIOUSLY LOADED ROUTE GRAPH CONNECTION UPDATED, scheduling update into the future: " + connection);
-
-                                    Set<Object> dirtyNodes = new THashSet<Object>(4);
-                                    IElement mappedElement = getMappedElement(connection);
-                                    ConnectionEntity ce = mappedElement.getHint(ElementHints.KEY_CONNECTION_ENTITY);
-                                    if (ce != null) {
-                                        for (Connection conn : ce.getTerminalConnections(null)) {
-                                            Object o = conn.node.getHint(ElementHints.KEY_OBJECT);
-                                            if (o != null) {
-                                                dirtyNodes.add(o);
-                                                if (DebugPolicy.DEBUG_CONNECTION_LISTENER)
-                                                    System.out.println("Marked connectivity dirty for node: " + conn.node);
-                                            }
-                                        }
-                                    }
+                                LoadRouteGraphConnectionListener loadListener = new LoadRouteGraphConnectionListener(canvasListenerSupport, connection);
 
-                                    offerGraphUpdate( routeGraphConnectionUpdater(connection, loaded, dirtyNodes) );
-                                }
-                            }
-                        };
+                                Tuple3 t = graph.syncRequest(new ConnectionRequest2(canvas, diagram, connection, errorHandler));
+                                IElement e = (IElement)t.c0;
+                                ElementClass ec = (ElementClass)t.c1;
+                                org.simantics.diagram.adapter.ElementFactory ef = (org.simantics.diagram.adapter.ElementFactory)t.c2;
 
-                        graph.asyncRequest(new ConnectionRequest(canvas, diagram, connection, errorHandler, loadListener), new Procedure<IElement>() {
-                            @Override
-                            public void execute(final IElement e) {
                                 if (e == null)
                                     return;
 
@@ -2025,12 +2160,14 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                     addedElementMap.put(connection, e);
                                     addedRouteGraphConnectionMap.put(connection, e);
                                 }
+
+                                graph.syncRequest(new LoadRequest(canvas, diagram, ef, ec, connection), loadListener);
+
                             }
-                            @Override
-                            public void exception(Throwable throwable) {
-                                error(throwable);
-                            }
+
                         });
+
+
                         break;
                     }
                     case REMOVED: {
@@ -2039,6 +2176,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                             removedRouteGraphConnections.add(e);
                         break;
                     }
+                    default:
                 }
             }
         }
@@ -2052,7 +2190,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             return assertMappedConnection(connection);
         }
 
-        void processBranchPoints(AsyncReadGraph graph) {
+        void processBranchPoints(AsyncReadGraph graph) throws DatabaseException {
             for (Map.Entry<Resource, Change> entry : changes.branchPoints.entrySet()) {
 
                 final Resource element = entry.getKey();
@@ -2073,10 +2211,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                 });
 
                             Listener<IElement> loadListener = new DisposableListener<IElement>(canvasListenerSupport) {
-                               @Override
-                               public String toString() {
-                                       return "processBranchPoints for " + element;
-                               }
+
+                                @Override
+                                public String toString() {
+                                    return "processBranchPoints for " + element;
+                                }
                                 @Override
                                 public void execute(IElement loaded) {
                                     // Invoked when the element has been loaded.
@@ -2118,16 +2257,6 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                             graph.asyncRequest(new NodeRequest(canvas, diagram, element, loadListener), new AsyncProcedure<IElement>() {
                                 @Override
                                 public void execute(AsyncReadGraph graph, IElement e) {
-                                    if (e != null) {
-                                        mapElement(element, e);
-                                        synchronized (GraphToDiagramUpdater.this) {
-                                            addedBranchPoints.add(e);
-                                            addedElementMap.put(element, e);
-                                            ConnectionEntityImpl ce = getConnectionEntity(element);
-                                            e.setHint(ElementHints.KEY_CONNECTION_ENTITY, ce);
-                                            e.setHint(ElementHints.KEY_PARENT_ELEMENT, ce.getConnectionElement());
-                                        }
-                                    }
                                 }
 
                                 @Override
@@ -2141,7 +2270,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     case REMOVED: {
                         IElement e = getMappedElement(element);
                         if (DebugPolicy.DEBUG_NODE_LOAD)
-                            graph.asyncRequest(new ReadRequest() {
+                            graph.syncRequest(new ReadRequest() {
                                 @Override
                                 public void run(ReadGraph graph) throws DatabaseException {
                                     System.out.println("    EXTERNALLY REMOVED BRANCH POINT: "
@@ -2154,11 +2283,12 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         }
                         break;
                     }
+                    default:
                 }
             }
         }
 
-        void processConnectionSegments(AsyncReadGraph graph) {
+        void processConnectionSegments(AsyncReadGraph graph) throws DatabaseException {
             ConnectionSegmentAdapter adapter = connectionSegmentAdapter;
 
             for (Map.Entry<EdgeResource, Change> entry : changes.connectionSegments.entrySet()) {
@@ -2178,13 +2308,13 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                                     }
                                 });
 
-                            graph.asyncRequest(new EdgeRequest(canvas, errorHandler, canvasListenerSupport, diagram, adapter, seg), new AsyncProcedure<IElement>() {
+                            graph.asyncRequest(new EdgeRequest(GraphToDiagramSynchronizer.this, canvas, errorHandler, canvasListenerSupport, diagram, adapter, seg), new AsyncProcedure<IElement>() {
                                 @Override
                                 public void execute(AsyncReadGraph graph, IElement e) {
                                     if (DebugPolicy.DEBUG_EDGE_LOAD)
-                                        System.out.println("ADDED EDGE LOADED: " + e);
+                                        System.out.println("ADDED EDGE LOADED: " + e + " " + seg);
+
                                     if (e != null) {
-                                        mapElement(seg, e);
                                         synchronized (GraphToDiagramUpdater.this) {
                                             addedConnectionSegments.add(e);
                                             addedElementMap.put(seg, e);
@@ -2218,6 +2348,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         }
                         break;
                     }
+                    default:
                 }
             }
         }
@@ -2256,13 +2387,15 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             if (changes.isEmpty())
                 return;
 
+            ITask threadLog = ThreadLogger.task("processNodes");
+            
             // NOTE: This order is important.
             Object task = Timing.BEGIN("processNodesConnections");
             //System.out.println("---- PROCESS NODES & CONNECTIONS BEGIN");
             if (!changes.elements.isEmpty()) {
                 graph.syncRequest(new AsyncReadRequest() {
                     @Override
-                    public void run(AsyncReadGraph graph) {
+                    public void run(AsyncReadGraph graph) throws DatabaseException {
                         processNodes(graph);
                     }
                     @Override
@@ -2273,13 +2406,21 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             }
             //System.out.println("---- PROCESS NODES & CONNECTIONS END");
 
+            threadLog.finish();
+            
+            threadLog = ThreadLogger.task("processConnections");
+
             processConnections();
 
+            threadLog.finish();
+
+            threadLog = ThreadLogger.task("processBranchPoints");
+
             //System.out.println("---- PROCESS BRANCH POINTS BEGIN");
             if (!changes.branchPoints.isEmpty()) {
                 graph.syncRequest(new AsyncReadRequest() {
                     @Override
-                    public void run(AsyncReadGraph graph) {
+                    public void run(AsyncReadGraph graph) throws DatabaseException {
                         processBranchPoints(graph);
                     }
                     @Override
@@ -2287,17 +2428,23 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         return "processBranchPoints";
                     }
                 });
+
             }
             //System.out.println("---- PROCESS BRANCH POINTS END");
 
+            threadLog.finish();
+
             Timing.END(task);
+
+            threadLog = ThreadLogger.task("processConnectionSegments");
+
             task = Timing.BEGIN("processConnectionSegments");
 
             //System.out.println("---- PROCESS CONNECTION SEGMENTS BEGIN");
             if (!changes.connectionSegments.isEmpty()) {
                 graph.syncRequest(new AsyncReadRequest() {
                     @Override
-                    public void run(AsyncReadGraph graph) {
+                    public void run(AsyncReadGraph graph) throws DatabaseException {
                         processConnectionSegments(graph);
                     }
                     @Override
@@ -2308,13 +2455,17 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             }
             //System.out.println("---- PROCESS CONNECTION SEGMENTS END");
 
+            threadLog.finish();
+
             Timing.END(task);
 
+            threadLog = ThreadLogger.task("processRouteGraphConnections");
+
             task = Timing.BEGIN("processRouteGraphConnections");
             if (!changes.routeGraphConnections.isEmpty()) {
                 graph.syncRequest(new AsyncReadRequest() {
                     @Override
-                    public void run(AsyncReadGraph graph) {
+                    public void run(AsyncReadGraph graph) throws DatabaseException {
                         processRouteGraphConnections(graph);
                     }
                     @Override
@@ -2325,6 +2476,8 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             }
             Timing.END(task);
 
+            threadLog.finish();
+
             //System.out.println("---- AFTER LOADING");
             //for (IElement e : addedElements)
             //    System.out.println("    ADDED ELEMENT: " + e);
@@ -2332,7 +2485,12 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
             //    System.out.println("    ADDED BRANCH POINTS: " + e);
 
             task = Timing.BEGIN("executeDeferredLoaders");
+            threadLog = ThreadLogger.task("executeDeferredLoaders");
+            
             executeDeferredLoaders(graph);
+
+            threadLog.finish();
+
             Timing.END(task);
         }
 
@@ -2373,17 +2531,15 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         return "defaultConnectionSegmentAdapter";
                     }
                 }, new DisposableListener<IElement>(listenerSupport) {
-                       
-                       @Override
-                       public String toString() {
-                               return "DefaultConnectionSegmentAdapter listener for " + edge;
-                       }
-                       
+                    
+                    @Override
+                    public String toString() {
+                        return "DefaultConnectionSegmentAdapter listener for " + edge;
+                    }
+                    
                     @Override
                     public void execute(IElement loaded) {
                         // Invoked when the element has been loaded.
-                        if (DebugPolicy.DEBUG_EDGE_LISTENER)
-                            System.out.println("EDGE LoadListener for " + loaded);
 
                         if (loaded == null) {
                             disposeListener();
@@ -2391,6 +2547,10 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                         }
 
                         Object data = loaded.getHint(ElementHints.KEY_OBJECT);
+
+                        if (DebugPolicy.DEBUG_EDGE_LISTENER)
+                            System.out.println("EDGE LoadListener for " + loaded + " " + data);
+
                         if (addedElementMap.containsKey(data)) {
                             // This element was just loaded, in
                             // which case its hints need to
@@ -2474,13 +2634,18 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     return;
                 }
 
-                // NOTICE: Layer information is loaded from the connection entity resource
-                // that is shared by all segments of the same connection.
-                ElementFactoryUtil.loadLayersForElement(graph, layerManager, diagram, edge, info.connection,
-                        new AsyncProcedureAdapter<IElement>() {
+                graph.syncRequest(new AsyncReadRequest() {
                     @Override
-                    public void exception(AsyncReadGraph graph, Throwable t) {
-                        error("failed to load layers for connection segment", t);
+                    public void run(AsyncReadGraph graph) {
+                        // NOTICE: Layer information is loaded from the connection entity resource
+                        // that is shared by all segments of the same connection.
+                        ElementFactoryUtil.loadLayersForElement(graph, layerManager, diagram, edge, info.connection,
+                                new AsyncProcedureAdapter<IElement>() {
+                            @Override
+                            public void exception(AsyncReadGraph graph, Throwable t) {
+                                error("failed to load layers for connection segment", t);
+                            }
+                        });
                     }
                 });
 
@@ -2628,8 +2793,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
 
                 diagramUpdateLock.lock();
                 try {
-                    if (DebugPolicy.DEBUG_DIAGRAM_UPDATE)
+                    if (DebugPolicy.DEBUG_DIAGRAM_UPDATE) {
                         System.out.println("In diagramGraphUpdater:");
+                        System.out.println("-content = " + content);
+                        System.out.println("-previousContent = " + previousContent);
+                    }
 
                     // Find out what has changed since the last query.
                     Object task = Timing.BEGIN("diagramContentDifference");
@@ -2650,9 +2818,9 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                     GraphToDiagramUpdater updater = new GraphToDiagramUpdater(lastContent, content, changes);
                     GraphToDiagramSynchronizer.this.currentUpdater = updater;
                     try {
-                       updater.process(graph);
+                        updater.process(graph);
                     } finally {
-                       GraphToDiagramSynchronizer.this.currentUpdater = null;
+                        GraphToDiagramSynchronizer.this.currentUpdater = null;
                     }
                     Timing.END(task);
 
@@ -3413,7 +3581,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID
                 Resource resource = ElementUtils.adapt(ec, Resource.class);
                 if (resource == null) {
                     pass = false;
-                    new Exception("Attempted to add an element to the diagram that is not adaptable to Resource: " + e + ", class: " + ec).printStackTrace();
+                    LOGGER.error("", new Exception("Attempted to add an element to the diagram that is not adaptable to Resource: " + e + ", class: " + ec));
                 }
 
                 // Sanity check connection hints