X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.structural.synchronization%2Fsrc%2Forg%2Fsimantics%2Fstructural%2Fsynchronization%2Fbase2%2FAbstractSynchronizationEventHandler.java;fp=bundles%2Forg.simantics.structural.synchronization%2Fsrc%2Forg%2Fsimantics%2Fstructural%2Fsynchronization%2Fbase2%2FAbstractSynchronizationEventHandler.java;h=8dc051a7a3e7894e2cc636c806368ddd8ce26b1d;hp=e1277326054d7339dc7e4f3fad1f0d93364822a3;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base2/AbstractSynchronizationEventHandler.java b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base2/AbstractSynchronizationEventHandler.java index e12773260..8dc051a7a 100644 --- a/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base2/AbstractSynchronizationEventHandler.java +++ b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base2/AbstractSynchronizationEventHandler.java @@ -1,286 +1,286 @@ -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; - -/** - *

A partial implementation of SynchronizationEventHandler that contains all generic - * logic for synchronization that is not domain specific. - * - *

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 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 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 newChildMap); - - /** - *

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. - * - *

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); - - /** - *

Updates the component based on the given name, type, properties and connections. - * - *

When updating has been finished (this may happen after this method has been finished) - * the method {@link #componentUpdated} must be called. - * - *

The method may use {@link resolveReference} to resolve connection point references. - */ - protected abstract void updateComponent(Component component, String name, - String typeId, Collection properties, - Collection 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 mayBeUpdated = new THashSet(); - - /** - * 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>> pendingResolves = - new THashMap>>(); - - /** - * This set contains all components that currently have no parents and will be removed - * at the end of synchronization. - */ - private THashSet pendingRemoval = new THashSet(); - - private static class PendingResolve { - 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 properties, - Collection connections, Collection 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 newChildMap = - new THashMap(); - 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 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>(2)); - } - - /** - * Signals that the component is updated so far that its connection may be resolved - * (with resolveConnectionPointLocally). - */ - public void componentUpdated(Component component) { - ArrayList> resolves = pendingResolves.remove(component); - if(resolves != null) - for(PendingResolve 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> pendingList = pendingResolves.get(component); - if(pendingList != null) { - pendingList.add( - new PendingResolve( - component, connectionPoint.substring(pos), connectionResolvedCallback)); - return; - } - } break; - case '#': { - String segment = connectionPoint.substring(pos); - resolveConnectionPointLocally(component, segment, connectionResolvedCallback); - } return; - } - } - } -} +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; + +/** + *

A partial implementation of SynchronizationEventHandler that contains all generic + * logic for synchronization that is not domain specific. + * + *

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 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 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 newChildMap); + + /** + *

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. + * + *

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); + + /** + *

Updates the component based on the given name, type, properties and connections. + * + *

When updating has been finished (this may happen after this method has been finished) + * the method {@link #componentUpdated} must be called. + * + *

The method may use {@link resolveReference} to resolve connection point references. + */ + protected abstract void updateComponent(Component component, String name, + String typeId, Collection properties, + Collection 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 mayBeUpdated = new THashSet(); + + /** + * 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>> pendingResolves = + new THashMap>>(); + + /** + * This set contains all components that currently have no parents and will be removed + * at the end of synchronization. + */ + private THashSet pendingRemoval = new THashSet(); + + private static class PendingResolve { + 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 properties, + Collection connections, Collection 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 newChildMap = + new THashMap(); + 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 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>(2)); + } + + /** + * Signals that the component is updated so far that its connection may be resolved + * (with resolveConnectionPointLocally). + */ + public void componentUpdated(Component component) { + ArrayList> resolves = pendingResolves.remove(component); + if(resolves != null) + for(PendingResolve 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> pendingList = pendingResolves.get(component); + if(pendingList != null) { + pendingList.add( + new PendingResolve( + component, connectionPoint.substring(pos), connectionResolvedCallback)); + return; + } + } break; + case '#': { + String segment = connectionPoint.substring(pos); + resolveConnectionPointLocally(component, segment, connectionResolvedCallback); + } return; + } + } + } +}