+++ /dev/null
-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 org.simantics.structural.synchronization.utils.ComponentBase;
-import org.simantics.structural.synchronization.utils.ComponentFactory;
-import org.simantics.structural.synchronization.utils.MappingBase;
-import org.simantics.structural.synchronization.utils.Solver;
-import org.slf4j.Logger;
-
-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) {
- getLogger().error(description);
- }
-
- public void reportProblem(String description, Exception e) {
- getLogger().error(description, e);
- }
-
- 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;
- }
-
- public abstract Logger getLogger();
-}