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(); }