+++ /dev/null
-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ö
- */
-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;
- }
- }
- }
-}