]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/base/ReferenceResolverBase.java
Separate DB and non-DB code to different structural sync bundles
[simantics/platform.git] / bundles / org.simantics.structural.synchronization / src / org / simantics / structural / synchronization / base / ReferenceResolverBase.java
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 (file)
index 0000000..83ff7d7
--- /dev/null
@@ -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<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();
+}