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