]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base2/AbstractSynchronizationEventHandler.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.structural.synchronization / src / org / simantics / structural / synchronization / base2 / AbstractSynchronizationEventHandler.java
index e1277326054d7339dc7e4f3fad1f0d93364822a3..8dc051a7a3e7894e2cc636c806368ddd8ce26b1d 100644 (file)
-package org.simantics.structural.synchronization.base2;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Map;\r
-\r
-import org.simantics.databoard.util.URIStringUtils;\r
-import org.simantics.structural.synchronization.protocol.ChildInfo;\r
-import org.simantics.structural.synchronization.protocol.Connection;\r
-import org.simantics.structural.synchronization.protocol.SerializedVariable;\r
-import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;\r
-import org.simantics.structural.synchronization.protocol.SynchronizationException;\r
-\r
-/**\r
- * <p>A partial implementation of SynchronizationEventHandler that contains all generic\r
- * logic for synchronization that is not domain specific.\r
- * \r
- * <p>It is assumed that the current state of the simulator is stored in some tree-like\r
- * data structure and Component is the class that refers to currently instantiated components.\r
- * \r
- * @author Hannu Niemist&ouml;\r
- */\r
-public abstract class AbstractSynchronizationEventHandler<Component, ConnectionResolvedCallback> implements SynchronizationEventHandler {\r
-    /**\r
-     * Returns the root of the current configuration. It must not be null.\r
-     */\r
-    protected abstract Component getConfigurationRoot();\r
-    \r
-    /**\r
-     * Returns the parent of the component. It may be null, if the component\r
-     * is the configuration root or if it is detached or just created.\r
-     */\r
-    protected abstract Component getParent(Component component);\r
-    \r
-    /**\r
-     * Returns the map of current children of the component. The method may assume\r
-     * that the caller will not modify the map directly.\r
-     */\r
-    protected abstract Map<String, Component> getChildMap(Component component);\r
-    \r
-    /**\r
-     * Sets the children of the component. The method may assume that all components\r
-     * in the newChildMap are detached (i.e. do not have a parent).\r
-     */\r
-    protected abstract void setChildMap(Component component, THashMap<String, Component> newChildMap);\r
-\r
-    /**\r
-     * <p>Detaches the component from its parent. After calling this method\r
-     * getParent(component) and getChildMap(parent).get(name) should return null where \r
-     * parent is the old parent of the component and name the name of the component.\r
-     * \r
-     * <p>It is important that after detaching, the method {@link #getComponentByUid}\r
-     * still returns the component.\r
-     */\r
-    protected abstract void detachFromParent(Component component);\r
-    \r
-    /**\r
-     * Try to find the component by given uid. If it is not found, returns null.\r
-     */\r
-    protected abstract Component getComponentByUid(String uid);\r
-    \r
-    /**\r
-     * Creates a new component with the given uid. The method may\r
-     * assume that there is no existing component with the same uid.\r
-     * After the method returns, getComponentByUid must return the\r
-     * same component with the given uid.\r
-     */\r
-    protected abstract Component createComponent(String uid);\r
-    \r
-    /**\r
-     * Removes the component. The method may assume that the component\r
-     * has already been detached from its parent.\r
-     */\r
-    protected abstract void removeComponent(Component component);\r
-    \r
-    /**\r
-     * Calls connectionResolvedCallback with the result of resolving the given connection point\r
-     * of the given component. The method is called only for components whose updating is completed.\r
-     */\r
-    protected abstract void resolveConnectionPointLocally(Component component, String connectionPointName,\r
-            ConnectionResolvedCallback connectionResolvedCallback);\r
-    \r
-    /**\r
-     * <p>Updates the component based on the given name, type, properties and connections.\r
-     * \r
-     * <p>When updating has been finished (this may happen after this method has been finished)\r
-     * the method {@link #componentUpdated} must be called.\r
-     * \r
-     * <p>The method may use {@link resolveReference} to resolve connection point references.\r
-     */\r
-    protected abstract void updateComponent(Component component, String name,\r
-            String typeId, Collection<SerializedVariable> properties,\r
-            Collection<Connection> connections);\r
-    \r
-    /**\r
-     * The component that synchronizer is currently updating.\r
-     * If currentComponent=null, beginComponent method is not yet called\r
-     * or endComponent is called matching the first beginComponent call.\r
-     */\r
-    private Component currentComponent;\r
-    \r
-    /**\r
-     * The set of components which or whose children may still be\r
-     * updated by calling beginComponent during this synchronization.\r
-     */\r
-    private THashSet<Component> mayBeUpdated = new THashSet<Component>();\r
-    \r
-    /**\r
-     * The key set of this map contains all components in {@link #mayBeUpdated} and additionally\r
-     * components whose updating has began by beginComponent call, but that are not yet completely\r
-     * updated (for example because they are waiting other unresolved components). \r
-     */\r
-    private THashMap<Component, ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>> pendingResolves =\r
-            new THashMap<Component, ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>>();\r
-    \r
-    /**\r
-     * This set contains all components that currently have no parents and will be removed\r
-     * at the end of synchronization.\r
-     */\r
-    private THashSet<Component> pendingRemoval = new THashSet<Component>();\r
-    \r
-    private static class PendingResolve<Component, ConnectionResolvedCallback> {\r
-        public final Component component;\r
-        public final String connectionPoint;\r
-        public final ConnectionResolvedCallback connectionResolvedCallback;\r
-        \r
-        public PendingResolve(Component component, String connectionPoint,\r
-                ConnectionResolvedCallback connectionResolvedCallback) {\r
-            this.component = component;\r
-            this.connectionPoint = connectionPoint;\r
-            this.connectionResolvedCallback = connectionResolvedCallback;\r
-        }\r
-        \r
-        @Override\r
-        public String toString() {\r
-            return connectionPoint + "->" + connectionResolvedCallback;\r
-        }\r
-    }\r
-    \r
-    @Override\r
-    public void beginSynchronization() {\r
-        currentComponent = null;\r
-    }\r
-    \r
-    @Override\r
-    public void endSynchronization() {\r
-        if(currentComponent != null)\r
-            throw new SynchronizationException("beginComponent is called more often than endComponent at the end of synchronization.");\r
-        \r
-        for(Component component : pendingRemoval)\r
-            removeComponent(component);\r
-        pendingRemoval.clear();\r
-    }\r
-    \r
-    @Override\r
-    public void beginComponent(String name, String typeId,\r
-            Collection<SerializedVariable> properties,\r
-            Collection<Connection> connections, Collection<ChildInfo> children)\r
-            throws SynchronizationException {\r
-        if(currentComponent == null) {\r
-            currentComponent = getConfigurationRoot();\r
-            if(currentComponent == null)\r
-                throw new SynchronizationException("getConfiguration root returned null.");\r
-        }\r
-        else {\r
-            currentComponent = getChildMap(currentComponent).get(name);\r
-            if(currentComponent == null)\r
-                throw new SynchronizationException("Didn't find '"+name+"'. "\r
-                        + "It should have been mentioned as a child in the parent beginComponent method.");\r
-        }\r
-        \r
-        // Handle children\r
-        if(!getChildMap(currentComponent).isEmpty() || !children.isEmpty()){\r
-            THashMap<String, Component> newChildMap =\r
-                    new THashMap<String, Component>();\r
-            for(ChildInfo info : children) {\r
-                // Detach from the existing configuration the children with\r
-                // the right uids or create new components if uid is unknown.\r
-                Component component = getComponentByUid(info.uid);\r
-                if(component == null)\r
-                    component = createComponent(info.uid);\r
-                else {\r
-                    pendingRemoval.remove(component);\r
-                    detachFromParent(component);\r
-                }\r
-                newChildMap.put(info.name, component);\r
-                componentMayBeUpdated(component);\r
-            }\r
-    \r
-            // Put old children not detached in the previous phase\r
-            // to the pending removal set. They might have been\r
-            // moved somewhere else.\r
-            Map<String, Component> oldChildMap = getChildMap(currentComponent);\r
-            for(Component component : oldChildMap.values()) {\r
-                detachFromParent(component);\r
-                pendingRemoval.add(component);\r
-            }\r
-            setChildMap(currentComponent, newChildMap);\r
-        }\r
-        \r
-        // Update/create component itself\r
-        mayBeUpdated.remove(currentComponent);\r
-        updateComponent(currentComponent, name, typeId, properties, connections);\r
-    }\r
-    \r
-    @Override\r
-    public void endComponent() {\r
-        if(currentComponent == null)\r
-            throw new SynchronizationException("endComponent is called more often than beginComponent.");\r
-        \r
-        for(Component child : getChildMap(currentComponent).values())\r
-            if(mayBeUpdated.remove(child))\r
-                // clear pending status of components which will not change\r
-                // during synchronization\r
-                componentUpdated(child);\r
-        \r
-        currentComponent = getParent(currentComponent);\r
-    }\r
-    \r
-    private void componentMayBeUpdated(Component component) {\r
-        mayBeUpdated.add(component);\r
-        pendingResolves.put(component, new ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>(2));\r
-    }\r
-    \r
-    /**\r
-     * Signals that the component is updated so far that its connection may be resolved\r
-     * (with resolveConnectionPointLocally).\r
-     */\r
-    public void componentUpdated(Component component) {\r
-        ArrayList<PendingResolve<Component, ConnectionResolvedCallback>> resolves = pendingResolves.remove(component);\r
-        if(resolves != null)\r
-            for(PendingResolve<Component, ConnectionResolvedCallback> resolve : resolves) {\r
-                resolveConnectionPoint(resolve.component,\r
-                        resolve.connectionPoint,\r
-                        resolve.connectionResolvedCallback);\r
-            }\r
-    }\r
-    \r
-    /**\r
-     * Resolves relative connection point reference starting from the given component and calls\r
-     * ConnectionResolvedCallback with the resolved reference.\r
-     */\r
-    public void resolveConnectionPoint(Component component, String connectionPoint, ConnectionResolvedCallback connectionResolvedCallback) {\r
-        int pos = 0;\r
-        while(true) {\r
-            char c = connectionPoint.charAt(pos++);\r
-            switch(c) {\r
-            case '.':\r
-                component = getParent(component);\r
-                break;\r
-            case '/': {\r
-                int endPos = pos;\r
-                while(true) {\r
-                    c = connectionPoint.charAt(endPos);\r
-                    if(c == '/' || c == '#')\r
-                        break;\r
-                    ++endPos;\r
-                }\r
-                String segment = URIStringUtils.unescape(connectionPoint.substring(pos, endPos));\r
-                pos = endPos;\r
-                component = getChildMap(component).get(segment);\r
-                if(component == null) {\r
-                    String message = "Couldn't resolve " + connectionPoint +\r
-                            ", because child " + segment + " does not exist.";\r
-                    reportProblem(message);\r
-                    return;\r
-                }\r
-                ArrayList<PendingResolve<Component, ConnectionResolvedCallback>> pendingList = pendingResolves.get(component);\r
-                if(pendingList != null) {\r
-                    pendingList.add(\r
-                            new PendingResolve<Component, ConnectionResolvedCallback>(\r
-                                    component, connectionPoint.substring(pos), connectionResolvedCallback));\r
-                    return;\r
-                }\r
-            } break;\r
-            case '#': {\r
-                String segment = connectionPoint.substring(pos);\r
-                resolveConnectionPointLocally(component, segment, connectionResolvedCallback);\r
-            } return;\r
-            }\r
-        }\r
-    }\r
-}\r
+package org.simantics.structural.synchronization.base2;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.simantics.databoard.util.URIStringUtils;
+import org.simantics.structural.synchronization.protocol.ChildInfo;
+import org.simantics.structural.synchronization.protocol.Connection;
+import org.simantics.structural.synchronization.protocol.SerializedVariable;
+import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
+import org.simantics.structural.synchronization.protocol.SynchronizationException;
+
+/**
+ * <p>A partial implementation of SynchronizationEventHandler that contains all generic
+ * logic for synchronization that is not domain specific.
+ * 
+ * <p>It is assumed that the current state of the simulator is stored in some tree-like
+ * data structure and Component is the class that refers to currently instantiated components.
+ * 
+ * @author Hannu Niemist&ouml;
+ */
+public abstract class AbstractSynchronizationEventHandler<Component, ConnectionResolvedCallback> implements SynchronizationEventHandler {
+    /**
+     * Returns the root of the current configuration. It must not be null.
+     */
+    protected abstract Component getConfigurationRoot();
+    
+    /**
+     * Returns the parent of the component. It may be null, if the component
+     * is the configuration root or if it is detached or just created.
+     */
+    protected abstract Component getParent(Component component);
+    
+    /**
+     * Returns the map of current children of the component. The method may assume
+     * that the caller will not modify the map directly.
+     */
+    protected abstract Map<String, Component> getChildMap(Component component);
+    
+    /**
+     * Sets the children of the component. The method may assume that all components
+     * in the newChildMap are detached (i.e. do not have a parent).
+     */
+    protected abstract void setChildMap(Component component, THashMap<String, Component> newChildMap);
+
+    /**
+     * <p>Detaches the component from its parent. After calling this method
+     * getParent(component) and getChildMap(parent).get(name) should return null where 
+     * parent is the old parent of the component and name the name of the component.
+     * 
+     * <p>It is important that after detaching, the method {@link #getComponentByUid}
+     * still returns the component.
+     */
+    protected abstract void detachFromParent(Component component);
+    
+    /**
+     * Try to find the component by given uid. If it is not found, returns null.
+     */
+    protected abstract Component getComponentByUid(String uid);
+    
+    /**
+     * Creates a new component with the given uid. The method may
+     * assume that there is no existing component with the same uid.
+     * After the method returns, getComponentByUid must return the
+     * same component with the given uid.
+     */
+    protected abstract Component createComponent(String uid);
+    
+    /**
+     * Removes the component. The method may assume that the component
+     * has already been detached from its parent.
+     */
+    protected abstract void removeComponent(Component component);
+    
+    /**
+     * Calls connectionResolvedCallback with the result of resolving the given connection point
+     * of the given component. The method is called only for components whose updating is completed.
+     */
+    protected abstract void resolveConnectionPointLocally(Component component, String connectionPointName,
+            ConnectionResolvedCallback connectionResolvedCallback);
+    
+    /**
+     * <p>Updates the component based on the given name, type, properties and connections.
+     * 
+     * <p>When updating has been finished (this may happen after this method has been finished)
+     * the method {@link #componentUpdated} must be called.
+     * 
+     * <p>The method may use {@link resolveReference} to resolve connection point references.
+     */
+    protected abstract void updateComponent(Component component, String name,
+            String typeId, Collection<SerializedVariable> properties,
+            Collection<Connection> connections);
+    
+    /**
+     * The component that synchronizer is currently updating.
+     * If currentComponent=null, beginComponent method is not yet called
+     * or endComponent is called matching the first beginComponent call.
+     */
+    private Component currentComponent;
+    
+    /**
+     * The set of components which or whose children may still be
+     * updated by calling beginComponent during this synchronization.
+     */
+    private THashSet<Component> mayBeUpdated = new THashSet<Component>();
+    
+    /**
+     * The key set of this map contains all components in {@link #mayBeUpdated} and additionally
+     * components whose updating has began by beginComponent call, but that are not yet completely
+     * updated (for example because they are waiting other unresolved components). 
+     */
+    private THashMap<Component, ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>> pendingResolves =
+            new THashMap<Component, ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>>();
+    
+    /**
+     * This set contains all components that currently have no parents and will be removed
+     * at the end of synchronization.
+     */
+    private THashSet<Component> pendingRemoval = new THashSet<Component>();
+    
+    private static class PendingResolve<Component, ConnectionResolvedCallback> {
+        public final Component component;
+        public final String connectionPoint;
+        public final ConnectionResolvedCallback connectionResolvedCallback;
+        
+        public PendingResolve(Component component, String connectionPoint,
+                ConnectionResolvedCallback connectionResolvedCallback) {
+            this.component = component;
+            this.connectionPoint = connectionPoint;
+            this.connectionResolvedCallback = connectionResolvedCallback;
+        }
+        
+        @Override
+        public String toString() {
+            return connectionPoint + "->" + connectionResolvedCallback;
+        }
+    }
+    
+    @Override
+    public void beginSynchronization() {
+        currentComponent = null;
+    }
+    
+    @Override
+    public void endSynchronization() {
+        if(currentComponent != null)
+            throw new SynchronizationException("beginComponent is called more often than endComponent at the end of synchronization.");
+        
+        for(Component component : pendingRemoval)
+            removeComponent(component);
+        pendingRemoval.clear();
+    }
+    
+    @Override
+    public void beginComponent(String name, String typeId,
+            Collection<SerializedVariable> properties,
+            Collection<Connection> connections, Collection<ChildInfo> children)
+            throws SynchronizationException {
+        if(currentComponent == null) {
+            currentComponent = getConfigurationRoot();
+            if(currentComponent == null)
+                throw new SynchronizationException("getConfiguration root returned null.");
+        }
+        else {
+            currentComponent = getChildMap(currentComponent).get(name);
+            if(currentComponent == null)
+                throw new SynchronizationException("Didn't find '"+name+"'. "
+                        + "It should have been mentioned as a child in the parent beginComponent method.");
+        }
+        
+        // Handle children
+        if(!getChildMap(currentComponent).isEmpty() || !children.isEmpty()){
+            THashMap<String, Component> newChildMap =
+                    new THashMap<String, Component>();
+            for(ChildInfo info : children) {
+                // Detach from the existing configuration the children with
+                // the right uids or create new components if uid is unknown.
+                Component component = getComponentByUid(info.uid);
+                if(component == null)
+                    component = createComponent(info.uid);
+                else {
+                    pendingRemoval.remove(component);
+                    detachFromParent(component);
+                }
+                newChildMap.put(info.name, component);
+                componentMayBeUpdated(component);
+            }
+    
+            // Put old children not detached in the previous phase
+            // to the pending removal set. They might have been
+            // moved somewhere else.
+            Map<String, Component> oldChildMap = getChildMap(currentComponent);
+            for(Component component : oldChildMap.values()) {
+                detachFromParent(component);
+                pendingRemoval.add(component);
+            }
+            setChildMap(currentComponent, newChildMap);
+        }
+        
+        // Update/create component itself
+        mayBeUpdated.remove(currentComponent);
+        updateComponent(currentComponent, name, typeId, properties, connections);
+    }
+    
+    @Override
+    public void endComponent() {
+        if(currentComponent == null)
+            throw new SynchronizationException("endComponent is called more often than beginComponent.");
+        
+        for(Component child : getChildMap(currentComponent).values())
+            if(mayBeUpdated.remove(child))
+                // clear pending status of components which will not change
+                // during synchronization
+                componentUpdated(child);
+        
+        currentComponent = getParent(currentComponent);
+    }
+    
+    private void componentMayBeUpdated(Component component) {
+        mayBeUpdated.add(component);
+        pendingResolves.put(component, new ArrayList<PendingResolve<Component, ConnectionResolvedCallback>>(2));
+    }
+    
+    /**
+     * Signals that the component is updated so far that its connection may be resolved
+     * (with resolveConnectionPointLocally).
+     */
+    public void componentUpdated(Component component) {
+        ArrayList<PendingResolve<Component, ConnectionResolvedCallback>> resolves = pendingResolves.remove(component);
+        if(resolves != null)
+            for(PendingResolve<Component, ConnectionResolvedCallback> resolve : resolves) {
+                resolveConnectionPoint(resolve.component,
+                        resolve.connectionPoint,
+                        resolve.connectionResolvedCallback);
+            }
+    }
+    
+    /**
+     * Resolves relative connection point reference starting from the given component and calls
+     * ConnectionResolvedCallback with the resolved reference.
+     */
+    public void resolveConnectionPoint(Component component, String connectionPoint, ConnectionResolvedCallback connectionResolvedCallback) {
+        int pos = 0;
+        while(true) {
+            char c = connectionPoint.charAt(pos++);
+            switch(c) {
+            case '.':
+                component = getParent(component);
+                break;
+            case '/': {
+                int endPos = pos;
+                while(true) {
+                    c = connectionPoint.charAt(endPos);
+                    if(c == '/' || c == '#')
+                        break;
+                    ++endPos;
+                }
+                String segment = URIStringUtils.unescape(connectionPoint.substring(pos, endPos));
+                pos = endPos;
+                component = getChildMap(component).get(segment);
+                if(component == null) {
+                    String message = "Couldn't resolve " + connectionPoint +
+                            ", because child " + segment + " does not exist.";
+                    reportProblem(message);
+                    return;
+                }
+                ArrayList<PendingResolve<Component, ConnectionResolvedCallback>> pendingList = pendingResolves.get(component);
+                if(pendingList != null) {
+                    pendingList.add(
+                            new PendingResolve<Component, ConnectionResolvedCallback>(
+                                    component, connectionPoint.substring(pos), connectionResolvedCallback));
+                    return;
+                }
+            } break;
+            case '#': {
+                String segment = connectionPoint.substring(pos);
+                resolveConnectionPointLocally(component, segment, connectionResolvedCallback);
+            } return;
+            }
+        }
+    }
+}