]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/base/SynchronizationEventHandlerBase.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.structural.synchronization.client / src / org / simantics / structural / synchronization / base / SynchronizationEventHandlerBase.java
index 85023422e6d5a6fbc6d1cfe889e723bec8b53e54..d2eb50f22e30ee1a47448255bb73d98be81aa7a7 100644 (file)
-package org.simantics.structural.synchronization.base;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.util.ArrayDeque;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Map;\r
-import java.util.Queue;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.structural.synchronization.internal.Policy;\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
- * Handles synchronization events by updating the simulator designated by the\r
- * provided {@link Solver} instance.\r
- * \r
- * @author Hannu Niemistö\r
- */\r
-public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {\r
-\r
-    public static final boolean TRACE_EVENTS = false;\r
-    \r
-    public final Solver solver;\r
-    protected final SolverNameUtil nameUtil;\r
-    protected final MappingBase<T> mapping;\r
-    final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;\r
-    final ComponentFactory<T> componentFactory;\r
-    public final ReferenceResolverBase<T> resolver;\r
-    private boolean didChanges = false;\r
-\r
-    protected T component; // Current active component\r
-    THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();\r
-    Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();\r
-    protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();\r
-    \r
-    /**\r
-     * This is a set of components satisfying the following conditions\r
-     * <ul>\r
-     *     <li>beginComponent is called for their parents\r
-     *     <li>endComponent is not yet called for their parents\r
-     *     <li>beginComponent is not yet called for them\r
-     * </ul>\r
-     */\r
-    THashSet<T> potentiallyUpdatedComponents = new THashSet<>();\r
-\r
-    public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,\r
-            ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {\r
-        this.solver = solver;\r
-        this.nameUtil = nameUtil;\r
-        this.mapping = mapping;\r
-        this.componentFactory = componentFactory;\r
-        this.moduleUpdaterFactory = moduleUpdaterFactory;\r
-        this.resolver = resolver;\r
-    }\r
-    \r
-    @Override\r
-    public void beginSynchronization() {\r
-        if(TRACE_EVENTS) {\r
-            System.out.println("beginSynchronization()");\r
-            //mapping.printUidMap();\r
-        }\r
-        component = null;\r
-    }\r
-    \r
-    @Override\r
-    public void endSynchronization() {\r
-        try {\r
-            if(TRACE_EVENTS)\r
-                System.out.println("endSynchronization()");\r
-            if(component != null)\r
-                throw new SynchronizationException("beginComponent/endComponent calls do not match.");\r
-            \r
-            resolver.resolvePendingSelfReferences();\r
-            resolver.printPending();\r
-            \r
-            // Do removals\r
-            mapping.removePending(solver);\r
-            \r
-            // Post synchronization actions\r
-            Runnable action;\r
-            while((action = postSynchronizationActions.poll()) != null)\r
-                action.run();\r
-\r
-            // Rename modules to suggested names where possible.\r
-            nameUtil.applySuggestedNames((creationName, newName) -> {\r
-                ComponentBase<T> component = solverComponentNameToComponent.get(creationName);\r
-                if (component != null) {\r
-                    component.solverComponentName = newName;\r
-                }\r
-            });\r
-            solverComponentNameToComponent.clear();\r
-        } catch(Throwable e) {\r
-            Policy.logError(e);\r
-            throw new SynchronizationException(e);\r
-        }\r
-    }\r
-\r
-    private boolean isAttached(Collection<SerializedVariable> properties) {\r
-        for(SerializedVariable property : properties)\r
-            if(property.name.equals("IsAttached"))\r
-                try {\r
-                    return (Boolean)property.value.getValue(Bindings.BOOLEAN);\r
-                } catch (AdaptException e) {\r
-                    throw new SynchronizationException(e);\r
-                }\r
-        return false;\r
-    }\r
-    \r
-    private boolean isDesynchronized(Collection<SerializedVariable> properties) {\r
-        for(SerializedVariable property : properties)\r
-            if(property.name.equals("IsDesynchronized"))\r
-                try {\r
-                    return (Boolean)property.value.getValue(Bindings.BOOLEAN);\r
-                } catch (AdaptException e) {\r
-                    throw new SynchronizationException(e);\r
-                }\r
-        return false;\r
-    }\r
-    \r
-    @Override\r
-    public void beginComponent(String name, String typeId,\r
-            Collection<SerializedVariable> properties,\r
-            Collection<Connection> connections,\r
-            Collection<ChildInfo> children)\r
-                    throws SynchronizationException {\r
-        try {\r
-            if(TRACE_EVENTS) {\r
-                System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");\r
-                if(!children.isEmpty()) {\r
-                    System.out.println("    Children:");\r
-                    for(ChildInfo child : children)\r
-                        System.out.println("        " + child.name + " " + child.uid);\r
-                }\r
-                if(!connections.isEmpty()) {\r
-                    System.out.println("    Connections:");\r
-                    for(Connection connection : connections)\r
-                        System.out.println("        " + connection.relation + " " + connection.connectionPoints);\r
-                }\r
-            }\r
-            \r
-            String parentSolverComponentName;\r
-            \r
-            // Finds the composite\r
-            if(component == null) {\r
-                name = "COMP_ROOT";\r
-                parentSolverComponentName = "";\r
-                component = mapping.getConfiguration();\r
-                component.setModuleId(solver.getId(name));\r
-                component.solverComponentName = name;\r
-            }\r
-            else {\r
-                parentSolverComponentName = component.solverComponentName;\r
-                component = component.getChild(name);\r
-                if(component == 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
-            potentiallyUpdatedComponents.remove(component);\r
-    \r
-            ModuleUpdaterBase<T> updater = null;\r
-            if(typeId != null) {\r
-                updater = moduleUpdaters.get(typeId);\r
-                if(updater == null)\r
-                    throw new SynchronizationException("Undefined typeId " + typeId + ".");\r
-            }\r
-            \r
-            // Handle composite\r
-            if(typeId == null || updater.isUserComponent || updater.isComposite) {\r
-                // Create or update a subprocess\r
-                int moduleId = component.getModuleId();\r
-                boolean justCreated = false;\r
-                if(isAttached(properties))\r
-                    ; // Subprocesses are not created for attached composites\r
-                else if(moduleId <= 0) {\r
-                    String subprocessName = nameUtil.getFreshName(\r
-                            parentSolverComponentName,\r
-                            getSubprocessName(name, properties));\r
-                    try {\r
-                       solver.addSubprocess(subprocessName);\r
-                    } catch(Exception e) {\r
-                       reportProblem("Exception while adding subprocess.", e);\r
-                    }\r
-                    moduleId = solver.getId(subprocessName);\r
-                    if(moduleId <= 0)\r
-                        throw new SynchronizationException("Failed to create a subprocess " + subprocessName);\r
-                    component.setModuleId(moduleId);\r
-\r
-                    // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites\r
-                    if(component.getParent() != null) {\r
-                       String parentName = solver.getName(component.getParent().getModuleId());\r
-                       solver.includeSubprocess(parentName, subprocessName);\r
-                       component.solverComponentName = subprocessName;\r
-                       solverComponentNameToComponent.put(subprocessName, component);\r
-                    }\r
-                    \r
-                    if(updater.isComposite) {\r
-                       final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);\r
-                       updater.create(context, properties, connections);\r
-                    }\r
-                    \r
-                    justCreated = true;\r
-                    \r
-                } else {\r
-                       \r
-                    component.solverComponentName = nameUtil.ensureNameIsVariationOf(\r
-                            parentSolverComponentName, moduleId,\r
-                            getSubprocessName(name, properties));\r
-                    \r
-                    if(updater.isComposite) {\r
-                       final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);\r
-                       updater.update(context, properties, connections);\r
-                    }\r
-                    \r
-                }\r
-                if(mapping.getTrustUids()) {\r
-                       // Create a new child map\r
-                       THashMap<String, T> newChildMap =\r
-                                       new THashMap<String, T>();\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
-                        T conf = mapping.detachOrCreateComponent(info.uid);\r
-                        newChildMap.put(info.name, conf);\r
-                        resolver.markPending(conf);\r
-                        potentiallyUpdatedComponents.add(conf);\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
-                    THashMap<String, T> oldChildMap =\r
-                            component.setChildMapAndReturnOld(newChildMap);\r
-                    resolver.unmarkPending(component);\r
-                    if(oldChildMap != null)\r
-                        for(T component : oldChildMap.values()) {\r
-                            component.clearParent();\r
-                            mapping.addPendingRemoval(component);\r
-                        }\r
-                }\r
-                // Alternative implementation when uids are not available.\r
-                else {\r
-                    // Create a new child map\r
-                    THashMap<String, T> newChildMap =\r
-                            new THashMap<String, T>();\r
-                    Map<String, T> oldChildMap =\r
-                            component.getChildMap();\r
-                    if(oldChildMap == null)\r
-                        oldChildMap = Collections.<String,T>emptyMap();\r
-                    for(ChildInfo info : children) {\r
-                        T conf = oldChildMap.remove(info.name);\r
-                        if(conf == null)\r
-                            conf = componentFactory.create(info.uid);\r
-                        else\r
-                            conf.uid = info.uid;\r
-                        newChildMap.put(info.name, conf);\r
-                        resolver.markPending(conf);\r
-                    }\r
-                    component.setChildMap(newChildMap);\r
-\r
-                    resolver.unmarkPending(component);\r
-                    if(oldChildMap != null)\r
-                        for(T component : oldChildMap.values()) {\r
-                            component.clearParent();\r
-                            mapping.addPendingRemoval(component);\r
-                        }\r
-                }\r
-\r
-                postCompositeAction(justCreated, properties, connections, updater);\r
-                \r
-            }\r
-            // Handle component\r
-            else {\r
-                if(!children.isEmpty())\r
-                    throw new SynchronizationException("Component with type " + typeId + " cannot have children.");\r
-                \r
-                boolean attached = isAttached(properties);\r
-                component.attached = attached;\r
-\r
-                // Create or update the component\r
-                final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);\r
-                int moduleId = component.getModuleId();\r
-                if(moduleId <= 0) {\r
-                    if(attached) {\r
-                        component.attached = true;\r
-                        context.setModuleName(name);\r
-                        context.setModuleId(solver.getId(name));\r
-                        if(context.getModuleId() <= 0)\r
-                            reportProblem("Didn't find attached module " + name + ".");\r
-                        else if(!isDesynchronized(properties))\r
-                            updater.update(context, properties, connections);\r
-                        setDidChanges();\r
-                    }\r
-                    else {\r
-                        component.attached = false;\r
-                        context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));\r
-                        context.addPostUpdateAction(new Runnable() {\r
-                            @Override\r
-                            public void run() {\r
-                                context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,\r
-                                        context.component.componentId, \r
-                                        context.component.uid);\r
-                            }\r
-                        });\r
-                        updater.create(context, properties, connections);\r
-                        solverComponentNameToComponent.put(context.getModuleName(), component);\r
-                    }\r
-                }\r
-                else if(!isDesynchronized(properties)) {\r
-                    context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,\r
-                            moduleId, name));\r
-                    updater.update(context, properties, connections);\r
-                }\r
-                else {\r
-                    resolver.unmarkPending(component);\r
-                }\r
-            }\r
-        } catch(Throwable e) {\r
-            Policy.logError(e);\r
-            throw new SynchronizationException(e);\r
-        }\r
-    }\r
-\r
-    private String getSubprocessName(String name,\r
-            Collection<SerializedVariable> properties) {\r
-        for(SerializedVariable property : properties)\r
-            if(property.name.equals("HasSubprocessName"))\r
-                try {\r
-                    String value = (String)property.value.getValue(Bindings.STRING);\r
-                    if (!value.isEmpty())\r
-                        return value;\r
-                } catch (AdaptException e) {\r
-                    // This is very improbable exception.\r
-                    // Just ignore it and return the name.\r
-                    e.printStackTrace();\r
-                    break;\r
-                }\r
-        return name;\r
-    }\r
-\r
-    @Override\r
-    public void endComponent() {\r
-        try {\r
-            if(TRACE_EVENTS)\r
-                System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");\r
-            if(component == null) return;\r
-            for(T child : component.getChildren())\r
-                if(potentiallyUpdatedComponents.remove(child))\r
-                    resolver.unmarkPending(child);\r
-            T parent = component.getParent();\r
-            if (parent == null && mapping.getConfiguration() != component)\r
-                throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");\r
-            component = parent;\r
-        } catch(Throwable e) {\r
-            Policy.logError(e);\r
-            throw new SynchronizationException(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void beginType(String id, Collection<SerializedVariable> properties) {\r
-        try {\r
-            /*if(TRACE_EVENTS)\r
-                System.out.println("beginType("+id+")");*/\r
-            ModuleUpdaterBase<T> updater;\r
-            try {\r
-                updater = moduleUpdaterFactory.createUpdater(id);\r
-            } catch (DatabaseException e) {\r
-                throw new RuntimeException(e);\r
-            }\r
-            if(updater == null)\r
-                throw new SynchronizationException("Failed to create module updater for id " + id + ".");\r
-            moduleUpdaters.put(id, updater);\r
-        } catch(Throwable e) {\r
-            Policy.logError(e);\r
-            throw new SynchronizationException(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void endType() {\r
-        /*if(TRACE_EVENTS)\r
-            System.out.println("endType()");*/\r
-    }\r
-\r
-    public boolean getDidChanges() {\r
-        return didChanges;\r
-    }\r
-\r
-    public void setDidChanges() {\r
-        didChanges = true;\r
-    }\r
-\r
-    public void reportProblem(String description) {\r
-        System.err.println(description);\r
-    }\r
-    \r
-    public void reportProblem(String description, Exception e) {\r
-        System.err.println(description);\r
-        e.printStackTrace();\r
-    }\r
-    \r
-    public void addPostSynchronizationAction(Runnable action) {\r
-        postSynchronizationActions.add(action);\r
-    }\r
-    \r
-    protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties, \r
-            Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception { \r
-    }\r
-\r
-    \r
-    public long getFromRevision() {\r
-        return mapping.currentRevision;\r
-    }\r
-}\r
+package org.simantics.structural.synchronization.base;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Queue;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.structural.synchronization.internal.Policy;
+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;
+
+/**
+ * Handles synchronization events by updating the simulator designated by the
+ * provided {@link Solver} instance.
+ * 
+ * @author Hannu Niemist&ouml;
+ */
+public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {
+
+    public static final boolean TRACE_EVENTS = false;
+    
+    public final Solver solver;
+    protected final SolverNameUtil nameUtil;
+    protected final MappingBase<T> mapping;
+    final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;
+    final ComponentFactory<T> componentFactory;
+    public final ReferenceResolverBase<T> resolver;
+    private boolean didChanges = false;
+
+    protected T component; // Current active component
+    THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();
+    Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();
+    protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();
+    
+    /**
+     * This is a set of components satisfying the following conditions
+     * <ul>
+     *     <li>beginComponent is called for their parents
+     *     <li>endComponent is not yet called for their parents
+     *     <li>beginComponent is not yet called for them
+     * </ul>
+     */
+    THashSet<T> potentiallyUpdatedComponents = new THashSet<>();
+
+    public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,
+            ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {
+        this.solver = solver;
+        this.nameUtil = nameUtil;
+        this.mapping = mapping;
+        this.componentFactory = componentFactory;
+        this.moduleUpdaterFactory = moduleUpdaterFactory;
+        this.resolver = resolver;
+    }
+    
+    @Override
+    public void beginSynchronization() {
+        if(TRACE_EVENTS) {
+            System.out.println("beginSynchronization()");
+            //mapping.printUidMap();
+        }
+        component = null;
+    }
+    
+    @Override
+    public void endSynchronization() {
+        try {
+            if(TRACE_EVENTS)
+                System.out.println("endSynchronization()");
+            if(component != null)
+                throw new SynchronizationException("beginComponent/endComponent calls do not match.");
+            
+            resolver.resolvePendingSelfReferences();
+            resolver.printPending();
+            
+            // Do removals
+            mapping.removePending(solver);
+            
+            // Post synchronization actions
+            Runnable action;
+            while((action = postSynchronizationActions.poll()) != null)
+                action.run();
+
+            // Rename modules to suggested names where possible.
+            nameUtil.applySuggestedNames((creationName, newName) -> {
+                ComponentBase<T> component = solverComponentNameToComponent.get(creationName);
+                if (component != null) {
+                    component.solverComponentName = newName;
+                }
+            });
+            solverComponentNameToComponent.clear();
+        } catch(Throwable e) {
+            Policy.logError(e);
+            throw new SynchronizationException(e);
+        }
+    }
+
+    private boolean isAttached(Collection<SerializedVariable> properties) {
+        for(SerializedVariable property : properties)
+            if(property.name.equals("IsAttached"))
+                try {
+                    return (Boolean)property.value.getValue(Bindings.BOOLEAN);
+                } catch (AdaptException e) {
+                    throw new SynchronizationException(e);
+                }
+        return false;
+    }
+    
+    private boolean isDesynchronized(Collection<SerializedVariable> properties) {
+        for(SerializedVariable property : properties)
+            if(property.name.equals("IsDesynchronized"))
+                try {
+                    return (Boolean)property.value.getValue(Bindings.BOOLEAN);
+                } catch (AdaptException e) {
+                    throw new SynchronizationException(e);
+                }
+        return false;
+    }
+    
+    @Override
+    public void beginComponent(String name, String typeId,
+            Collection<SerializedVariable> properties,
+            Collection<Connection> connections,
+            Collection<ChildInfo> children)
+                    throws SynchronizationException {
+        try {
+            if(TRACE_EVENTS) {
+                System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
+                if(!children.isEmpty()) {
+                    System.out.println("    Children:");
+                    for(ChildInfo child : children)
+                        System.out.println("        " + child.name + " " + child.uid);
+                }
+                if(!connections.isEmpty()) {
+                    System.out.println("    Connections:");
+                    for(Connection connection : connections)
+                        System.out.println("        " + connection.relation + " " + connection.connectionPoints);
+                }
+            }
+            
+            String parentSolverComponentName;
+            
+            // Finds the composite
+            if(component == null) {
+                name = "COMP_ROOT";
+                parentSolverComponentName = "";
+                component = mapping.getConfiguration();
+                component.setModuleId(solver.getId(name));
+                component.solverComponentName = name;
+            }
+            else {
+                parentSolverComponentName = component.solverComponentName;
+                component = component.getChild(name);
+                if(component == null)
+                    throw new SynchronizationException("Didn't find '"+name+"'. "
+                            + "It should have been mentioned as a child in the parent beginComponent method.");
+            }
+            
+            potentiallyUpdatedComponents.remove(component);
+    
+            ModuleUpdaterBase<T> updater = null;
+            if(typeId != null) {
+                updater = moduleUpdaters.get(typeId);
+                if(updater == null)
+                    throw new SynchronizationException("Undefined typeId " + typeId + ".");
+            }
+            
+            // Handle composite
+            if(typeId == null || updater.isUserComponent || updater.isComposite) {
+                // Create or update a subprocess
+                int moduleId = component.getModuleId();
+                boolean justCreated = false;
+                if(isAttached(properties))
+                    ; // Subprocesses are not created for attached composites
+                else if(moduleId <= 0) {
+                    String subprocessName = nameUtil.getFreshName(
+                            parentSolverComponentName,
+                            getSubprocessName(name, properties));
+                    try {
+                       solver.addSubprocess(subprocessName);
+                    } catch(Exception e) {
+                       reportProblem("Exception while adding subprocess.", e);
+                    }
+                    moduleId = solver.getId(subprocessName);
+                    if(moduleId <= 0)
+                        throw new SynchronizationException("Failed to create a subprocess " + subprocessName);
+                    component.setModuleId(moduleId);
+
+                    // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites
+                    if(component.getParent() != null) {
+                       String parentName = solver.getName(component.getParent().getModuleId());
+                       solver.includeSubprocess(parentName, subprocessName);
+                       component.solverComponentName = subprocessName;
+                       solverComponentNameToComponent.put(subprocessName, component);
+                    }
+                    
+                    if(updater.isComposite) {
+                       final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
+                       updater.create(context, properties, connections);
+                    }
+                    
+                    justCreated = true;
+                    
+                } else {
+                       
+                    component.solverComponentName = nameUtil.ensureNameIsVariationOf(
+                            parentSolverComponentName, moduleId,
+                            getSubprocessName(name, properties));
+                    
+                    if(updater.isComposite) {
+                       final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
+                       updater.update(context, properties, connections);
+                    }
+                    
+                }
+                if(mapping.getTrustUids()) {
+                       // Create a new child map
+                       THashMap<String, T> newChildMap =
+                                       new THashMap<String, T>();
+                    for(ChildInfo info : children) {
+                        // Detach from the existing configuration the children with
+                        // the right uids or create new components if uid is unknown.
+                        T conf = mapping.detachOrCreateComponent(info.uid);
+                        newChildMap.put(info.name, conf);
+                        resolver.markPending(conf);
+                        potentiallyUpdatedComponents.add(conf);
+                    }
+        
+                    // Put old children not detached in the previous phase
+                    // to the pending removal set. They might have been
+                    // moved somewhere else.
+                    THashMap<String, T> oldChildMap =
+                            component.setChildMapAndReturnOld(newChildMap);
+                    resolver.unmarkPending(component);
+                    if(oldChildMap != null)
+                        for(T component : oldChildMap.values()) {
+                            component.clearParent();
+                            mapping.addPendingRemoval(component);
+                        }
+                }
+                // Alternative implementation when uids are not available.
+                else {
+                    // Create a new child map
+                    THashMap<String, T> newChildMap =
+                            new THashMap<String, T>();
+                    Map<String, T> oldChildMap =
+                            component.getChildMap();
+                    if(oldChildMap == null)
+                        oldChildMap = Collections.<String,T>emptyMap();
+                    for(ChildInfo info : children) {
+                        T conf = oldChildMap.remove(info.name);
+                        if(conf == null)
+                            conf = componentFactory.create(info.uid);
+                        else
+                            conf.uid = info.uid;
+                        newChildMap.put(info.name, conf);
+                        resolver.markPending(conf);
+                    }
+                    component.setChildMap(newChildMap);
+
+                    resolver.unmarkPending(component);
+                    if(oldChildMap != null)
+                        for(T component : oldChildMap.values()) {
+                            component.clearParent();
+                            mapping.addPendingRemoval(component);
+                        }
+                }
+
+                postCompositeAction(justCreated, properties, connections, updater);
+                
+            }
+            // Handle component
+            else {
+                if(!children.isEmpty())
+                    throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
+                
+                boolean attached = isAttached(properties);
+                component.attached = attached;
+
+                // Create or update the component
+                final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
+                int moduleId = component.getModuleId();
+                if(moduleId <= 0) {
+                    if(attached) {
+                        component.attached = true;
+                        context.setModuleName(name);
+                        context.setModuleId(solver.getId(name));
+                        if(context.getModuleId() <= 0)
+                            reportProblem("Didn't find attached module " + name + ".");
+                        else if(!isDesynchronized(properties))
+                            updater.update(context, properties, connections);
+                        setDidChanges();
+                    }
+                    else {
+                        component.attached = false;
+                        context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
+                        context.addPostUpdateAction(new Runnable() {
+                            @Override
+                            public void run() {
+                                context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
+                                        context.component.componentId, 
+                                        context.component.uid);
+                            }
+                        });
+                        updater.create(context, properties, connections);
+                        solverComponentNameToComponent.put(context.getModuleName(), component);
+                    }
+                }
+                else if(!isDesynchronized(properties)) {
+                    context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
+                            moduleId, name));
+                    updater.update(context, properties, connections);
+                }
+                else {
+                    resolver.unmarkPending(component);
+                }
+            }
+        } catch(Throwable e) {
+            Policy.logError(e);
+            throw new SynchronizationException(e);
+        }
+    }
+
+    private String getSubprocessName(String name,
+            Collection<SerializedVariable> properties) {
+        for(SerializedVariable property : properties)
+            if(property.name.equals("HasSubprocessName"))
+                try {
+                    String value = (String)property.value.getValue(Bindings.STRING);
+                    if (!value.isEmpty())
+                        return value;
+                } catch (AdaptException e) {
+                    // This is very improbable exception.
+                    // Just ignore it and return the name.
+                    e.printStackTrace();
+                    break;
+                }
+        return name;
+    }
+
+    @Override
+    public void endComponent() {
+        try {
+            if(TRACE_EVENTS)
+                System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
+            if(component == null) return;
+            for(T child : component.getChildren())
+                if(potentiallyUpdatedComponents.remove(child))
+                    resolver.unmarkPending(child);
+            T parent = component.getParent();
+            if (parent == null && mapping.getConfiguration() != component)
+                throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
+            component = parent;
+        } catch(Throwable e) {
+            Policy.logError(e);
+            throw new SynchronizationException(e);
+        }
+    }
+
+    @Override
+    public void beginType(String id, Collection<SerializedVariable> properties) {
+        try {
+            /*if(TRACE_EVENTS)
+                System.out.println("beginType("+id+")");*/
+            ModuleUpdaterBase<T> updater;
+            try {
+                updater = moduleUpdaterFactory.createUpdater(id);
+            } catch (DatabaseException e) {
+                throw new RuntimeException(e);
+            }
+            if(updater == null)
+                throw new SynchronizationException("Failed to create module updater for id " + id + ".");
+            moduleUpdaters.put(id, updater);
+        } catch(Throwable e) {
+            Policy.logError(e);
+            throw new SynchronizationException(e);
+        }
+    }
+
+    @Override
+    public void endType() {
+        /*if(TRACE_EVENTS)
+            System.out.println("endType()");*/
+    }
+
+    public boolean getDidChanges() {
+        return didChanges;
+    }
+
+    public void setDidChanges() {
+        didChanges = true;
+    }
+
+    public void reportProblem(String description) {
+        System.err.println(description);
+    }
+    
+    public void reportProblem(String description, Exception e) {
+        System.err.println(description);
+        e.printStackTrace();
+    }
+    
+    public void addPostSynchronizationAction(Runnable action) {
+        postSynchronizationActions.add(action);
+    }
+    
+    protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties, 
+            Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception { 
+    }
+
+    
+    public long getFromRevision() {
+        return mapping.currentRevision;
+    }
+}