-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 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;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * Handles synchronization events by updating the simulator designated by the
+ * provided {@link Solver} instance.
+ *
+ * @author Hannu Niemistö
+ */
+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, updater.subprocessType);
+ } 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;
+ }
+}