--- /dev/null
+package org.simantics.structural.synchronization.base;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.procedure.TObjectObjectProcedure;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+\r
+import org.simantics.databoard.util.URIStringUtils;\r
+import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;\r
+\r
+abstract public class ReferenceResolverBase<T extends ComponentBase<T>> {\r
+ \r
+ protected SynchronizationEventHandler eventHandler;\r
+ protected Solver solver;\r
+ protected THashMap<T, ArrayList<PendingResolve<T>>> pendingResolves = new THashMap<T, ArrayList<PendingResolve<T>>>();\r
+\r
+ protected static class PendingResolve<T> {\r
+ public final T component;\r
+ public final String connectionPoint;\r
+ public final ModuleCallback moduleCallback;\r
+ \r
+ public PendingResolve(T component, String connectionPoint,\r
+ ModuleCallback moduleCallback) {\r
+ this.component = component;\r
+ this.connectionPoint = connectionPoint;\r
+ this.moduleCallback = moduleCallback;\r
+ }\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return connectionPoint + "->" + moduleCallback;\r
+ }\r
+ }\r
+ \r
+ public ReferenceResolverBase(Solver solver) {\r
+ this.solver = solver;\r
+ }\r
+\r
+ public void setEventHandler(SynchronizationEventHandler eventHandler) {\r
+ this.eventHandler = eventHandler;\r
+ }\r
+ \r
+ /**\r
+ * Marks that the component might be updated in this synchronization and\r
+ * therefore it may not be yet used for resolving references.\r
+ */\r
+ public void markPending(T component) {\r
+ //System.out.println("markPending: " + fullPathOfComponent(component));\r
+ pendingResolves.put(component, new ArrayList<PendingResolve<T>>(2));\r
+ }\r
+\r
+ /**\r
+ * Marks that the component is not modified anymore in this synchornization.\r
+ * This information is local, some children of the component may be marked\r
+ * pending.\r
+ */\r
+ public void unmarkPending(T component) {\r
+ //System.out.println("unmarkPending: " + fullPathOfComponent(component));\r
+ ArrayList<PendingResolve<T>> resolves = pendingResolves.remove(component);\r
+ if(resolves != null)\r
+ for(PendingResolve<T> resolve : resolves) {\r
+ resolveReference(resolve.component,\r
+ resolve.connectionPoint,\r
+ resolve.moduleCallback);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Tries to resolve the given relative reference and then calls the ModuleCallback.\r
+ */\r
+ public void resolveReference(T component, String connectionPoint, ModuleCallback moduleCallback) {\r
+ int pos = 0;\r
+ while(true) {\r
+ char c = connectionPoint.charAt(pos++);\r
+ switch(c) {\r
+ case '.':\r
+ component = component.getParent();\r
+ break;\r
+ case '/': {\r
+ int endPos = pos;\r
+ while(true) {\r
+ c = connectionPoint.charAt(endPos);\r
+ if(c == '/' || c == '#')\r
+ break;\r
+ ++endPos;\r
+ }\r
+ String segment = URIStringUtils.unescape(connectionPoint.substring(pos, endPos));\r
+ pos = endPos;\r
+ component = component.getChild(segment);\r
+ if(component == null) {\r
+ String message = "Couldn't resolve " + connectionPoint +\r
+ ", because child " + segment + " does not exist.";\r
+ if(eventHandler == null)\r
+ System.err.println(message);\r
+ else\r
+ eventHandler.reportProblem(message);\r
+ return;\r
+ }\r
+ ArrayList<PendingResolve<T>> pendingList = pendingResolves.get(component);\r
+ if(pendingList != null) {\r
+ pendingList.add(new PendingResolve<T>(component, connectionPoint.substring(pos), moduleCallback));\r
+ return;\r
+ }\r
+ } break;\r
+ case '#': {\r
+ String segment = connectionPoint.substring(pos);\r
+ moduleCallback.execute(resolveConnectionPoint(component.componentId, segment));\r
+ } return;\r
+ }\r
+ }\r
+ }\r
+\r
+ abstract public int resolveConnectionPoint(int moduleId, String connectionPoint);\r
+\r
+ private static void fullPathOfComponent(StringBuilder b, ComponentBase<?> component) {\r
+ if(component != null) {\r
+ fullPathOfComponent(b, component.parent);\r
+ b.append("/").append(component.solverComponentName);\r
+ }\r
+ }\r
+ \r
+ private static String fullPathOfComponent(ComponentBase<?> component) {\r
+ StringBuilder b = new StringBuilder();\r
+ fullPathOfComponent(b, component);\r
+ return b.toString();\r
+ }\r
+ \r
+ public void printPending() {\r
+ if(!pendingResolves.isEmpty()) {\r
+ final ArrayList<String> pending = new ArrayList<String>();\r
+ pendingResolves.forEachEntry(new TObjectObjectProcedure<T, ArrayList<PendingResolve<T>>>() {\r
+ @Override\r
+ public boolean execute(T a, ArrayList<PendingResolve<T>> b) {\r
+ //if(!b.isEmpty())\r
+ // System.out.println(" " + a.solverComponentName + " (" + a.uid + ", " + a.parent.solverComponentName + ") " + b.size());\r
+ pending.add(fullPathOfComponent(a) + " " + b);\r
+ return true;\r
+ }\r
+ });\r
+ Collections.sort(pending);\r
+ System.out.println("Still pending:");\r
+ for(String p : pending)\r
+ System.out.println(" " + p);\r
+ }\r
+ }\r
+\r
+ public void resolvePendingSelfReferences() {\r
+ // Can be customized in subclasses\r
+ }\r
+\r
+}\r