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