Merge "Utility function for claiming literals"
[simantics/platform.git] / bundles / org.simantics.structural.synchronization / src / org / simantics / structural / synchronization / base / ReferenceResolverBase.java
1 package org.simantics.structural.synchronization.base;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5
6 import org.simantics.databoard.util.URIStringUtils;
7 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
8 import org.simantics.structural.synchronization.utils.ComponentBase;
9 import org.simantics.structural.synchronization.utils.Solver;
10 import org.slf4j.Logger;
11
12 import gnu.trove.map.hash.THashMap;
13 import gnu.trove.procedure.TObjectObjectProcedure;
14
15 abstract public class ReferenceResolverBase<T extends ComponentBase<T>> {
16         
17     protected SynchronizationEventHandler eventHandler;
18     protected Solver solver;
19     protected THashMap<T, ArrayList<PendingResolve<T>>> pendingResolves = new THashMap<T, ArrayList<PendingResolve<T>>>();
20
21     protected static class PendingResolve<T> {
22         public final T component;
23         public final String connectionPoint;
24         public final ModuleCallback moduleCallback;
25         
26         public PendingResolve(T component, String connectionPoint,
27                 ModuleCallback moduleCallback) {
28             this.component = component;
29             this.connectionPoint = connectionPoint;
30             this.moduleCallback = moduleCallback;
31         }
32         
33         @Override
34         public String toString() {
35             return connectionPoint + "->" + moduleCallback;
36         }
37     }
38     
39     public ReferenceResolverBase(Solver solver) {
40         this.solver = solver;
41     }
42
43     public void setEventHandler(SynchronizationEventHandler eventHandler) {
44         this.eventHandler = eventHandler;
45     }
46     
47     /**
48      * Marks that the component might be updated in this synchronization and
49      * therefore it may not be yet used for resolving references.
50      */
51     public void markPending(T component) {
52         //System.out.println("markPending: " + fullPathOfComponent(component));
53         pendingResolves.put(component, new ArrayList<PendingResolve<T>>(2));
54     }
55
56     /**
57      * Marks that the component is not modified anymore in this synchornization.
58      * This information is local, some children of the component may be marked
59      * pending.
60      */
61     public void unmarkPending(T component) {
62         //System.out.println("unmarkPending: " + fullPathOfComponent(component));
63         ArrayList<PendingResolve<T>> resolves = pendingResolves.remove(component);
64         if(resolves != null)
65             for(PendingResolve<T> resolve : resolves) {
66                 resolveReference(resolve.component,
67                         resolve.connectionPoint,
68                         resolve.moduleCallback);
69             }
70     }
71
72     /**
73      * Tries to resolve the given relative reference and then calls the ModuleCallback.
74      */
75     public void resolveReference(T component, String connectionPoint, ModuleCallback moduleCallback) {
76         int pos = 0;
77         while(true) {
78             char c = connectionPoint.charAt(pos++);
79             switch(c) {
80             case '.':
81                 component = component.getParent();
82                 break;
83             case '/': {
84                 int endPos = pos;
85                 while(true) {
86                     c = connectionPoint.charAt(endPos);
87                     if(c == '/' || c == '#')
88                         break;
89                     ++endPos;
90                 }
91                 String segment = URIStringUtils.unescape(connectionPoint.substring(pos, endPos));
92                 pos = endPos;
93                 component = component.getChild(segment);
94                 if(component == null) {
95                     String message = "Couldn't resolve " + connectionPoint +
96                             ", because child " + segment + " does not exist.";
97                     if(eventHandler == null)
98                         getLogger().warn(message);
99                     else
100                         eventHandler.reportProblem(message);
101                     return;
102                 }
103                 ArrayList<PendingResolve<T>> pendingList = pendingResolves.get(component);
104                 if(pendingList != null) {
105                     pendingList.add(new PendingResolve<T>(component, connectionPoint.substring(pos), moduleCallback));
106                     return;
107                 }
108             } break;
109             case '#': {
110                 String segment = connectionPoint.substring(pos);
111                 moduleCallback.execute(resolveConnectionPoint(component.componentId, segment));
112             } return;
113             }
114         }
115     }
116
117     abstract public int resolveConnectionPoint(int moduleId, String connectionPoint);
118
119     private static void fullPathOfComponent(StringBuilder b, ComponentBase<?> component) {
120         if(component != null) {
121             fullPathOfComponent(b, component.getParent());
122             b.append("/").append(component.solverComponentName);
123         }
124     }
125     
126     private static String fullPathOfComponent(ComponentBase<?> component) {
127         StringBuilder b = new StringBuilder();
128         fullPathOfComponent(b, component);
129         return b.toString();
130     }
131     
132     public void printPending() {
133         if(!pendingResolves.isEmpty()) {
134             final ArrayList<String> pending = new ArrayList<String>();
135             pendingResolves.forEachEntry(new TObjectObjectProcedure<T, ArrayList<PendingResolve<T>>>() {
136                 @Override
137                 public boolean execute(T a, ArrayList<PendingResolve<T>> b) {
138                     //if(!b.isEmpty())
139                     //    System.out.println("    "  + a.solverComponentName + " (" + a.uid + ", " + a.parent.solverComponentName + ") " + b.size());
140                     pending.add(fullPathOfComponent(a) + " " + b);
141                     return true;
142                 }
143             });
144             Collections.sort(pending);
145             getLogger().info("Still pending:");
146             for(String p : pending)
147                 getLogger().info("    " + p);
148         }
149     }
150
151     public void resolvePendingSelfReferences() {
152         // Can be customized in subclasses
153     }
154     
155     public abstract Logger getLogger();
156 }