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%2Fbase%2FReferenceResolverBase.java;fp=bundles%2Forg.simantics.structural.synchronization%2Fsrc%2Forg%2Fsimantics%2Fstructural%2Fsynchronization%2Fbase%2FReferenceResolverBase.java;h=83ff7d72e659ea4788cd8e150bfcf0462662990a;hp=0000000000000000000000000000000000000000;hb=e4007b17057ff4acc2e900c5c811743b74f71f41;hpb=3fe6778c21d6437e90d08987de6dae7bca89bc6d diff --git a/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base/ReferenceResolverBase.java b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base/ReferenceResolverBase.java new file mode 100644 index 000000000..83ff7d72e --- /dev/null +++ b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base/ReferenceResolverBase.java @@ -0,0 +1,156 @@ +package org.simantics.structural.synchronization.base; + +import java.util.ArrayList; +import java.util.Collections; + +import org.simantics.databoard.util.URIStringUtils; +import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler; +import org.simantics.structural.synchronization.utils.ComponentBase; +import org.simantics.structural.synchronization.utils.Solver; +import org.slf4j.Logger; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.procedure.TObjectObjectProcedure; + +abstract public class ReferenceResolverBase> { + + protected SynchronizationEventHandler eventHandler; + protected Solver solver; + protected THashMap>> pendingResolves = new THashMap>>(); + + protected static class PendingResolve { + public final T component; + public final String connectionPoint; + public final ModuleCallback moduleCallback; + + public PendingResolve(T component, String connectionPoint, + ModuleCallback moduleCallback) { + this.component = component; + this.connectionPoint = connectionPoint; + this.moduleCallback = moduleCallback; + } + + @Override + public String toString() { + return connectionPoint + "->" + moduleCallback; + } + } + + public ReferenceResolverBase(Solver solver) { + this.solver = solver; + } + + public void setEventHandler(SynchronizationEventHandler eventHandler) { + this.eventHandler = eventHandler; + } + + /** + * Marks that the component might be updated in this synchronization and + * therefore it may not be yet used for resolving references. + */ + public void markPending(T component) { + //System.out.println("markPending: " + fullPathOfComponent(component)); + pendingResolves.put(component, new ArrayList>(2)); + } + + /** + * Marks that the component is not modified anymore in this synchornization. + * This information is local, some children of the component may be marked + * pending. + */ + public void unmarkPending(T component) { + //System.out.println("unmarkPending: " + fullPathOfComponent(component)); + ArrayList> resolves = pendingResolves.remove(component); + if(resolves != null) + for(PendingResolve resolve : resolves) { + resolveReference(resolve.component, + resolve.connectionPoint, + resolve.moduleCallback); + } + } + + /** + * Tries to resolve the given relative reference and then calls the ModuleCallback. + */ + public void resolveReference(T component, String connectionPoint, ModuleCallback moduleCallback) { + int pos = 0; + while(true) { + char c = connectionPoint.charAt(pos++); + switch(c) { + case '.': + component = component.getParent(); + 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 = component.getChild(segment); + if(component == null) { + String message = "Couldn't resolve " + connectionPoint + + ", because child " + segment + " does not exist."; + if(eventHandler == null) + getLogger().warn(message); + else + eventHandler.reportProblem(message); + return; + } + ArrayList> pendingList = pendingResolves.get(component); + if(pendingList != null) { + pendingList.add(new PendingResolve(component, connectionPoint.substring(pos), moduleCallback)); + return; + } + } break; + case '#': { + String segment = connectionPoint.substring(pos); + moduleCallback.execute(resolveConnectionPoint(component.componentId, segment)); + } return; + } + } + } + + abstract public int resolveConnectionPoint(int moduleId, String connectionPoint); + + private static void fullPathOfComponent(StringBuilder b, ComponentBase component) { + if(component != null) { + fullPathOfComponent(b, component.getParent()); + b.append("/").append(component.solverComponentName); + } + } + + private static String fullPathOfComponent(ComponentBase component) { + StringBuilder b = new StringBuilder(); + fullPathOfComponent(b, component); + return b.toString(); + } + + public void printPending() { + if(!pendingResolves.isEmpty()) { + final ArrayList pending = new ArrayList(); + pendingResolves.forEachEntry(new TObjectObjectProcedure>>() { + @Override + public boolean execute(T a, ArrayList> b) { + //if(!b.isEmpty()) + // System.out.println(" " + a.solverComponentName + " (" + a.uid + ", " + a.parent.solverComponentName + ") " + b.size()); + pending.add(fullPathOfComponent(a) + " " + b); + return true; + } + }); + Collections.sort(pending); + getLogger().info("Still pending:"); + for(String p : pending) + getLogger().info(" " + p); + } + } + + public void resolvePendingSelfReferences() { + // Can be customized in subclasses + } + + public abstract Logger getLogger(); +}