-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
+package org.simantics.structural.synchronization.base;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.procedure.TObjectObjectProcedure;
+
+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;
+
+abstract public class ReferenceResolverBase<T extends ComponentBase<T>> {
+
+ protected SynchronizationEventHandler eventHandler;
+ protected Solver solver;
+ protected THashMap<T, ArrayList<PendingResolve<T>>> pendingResolves = new THashMap<T, ArrayList<PendingResolve<T>>>();
+
+ protected static class PendingResolve<T> {
+ 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<PendingResolve<T>>(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<PendingResolve<T>> resolves = pendingResolves.remove(component);
+ if(resolves != null)
+ for(PendingResolve<T> 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<PendingResolve<T>> pendingList = pendingResolves.get(component);
+ if(pendingList != null) {
+ pendingList.add(new PendingResolve<T>(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<String> pending = new ArrayList<String>();
+ pendingResolves.forEachEntry(new TObjectObjectProcedure<T, ArrayList<PendingResolve<T>>>() {
+ @Override
+ public boolean execute(T a, ArrayList<PendingResolve<T>> 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();
+}