]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/Synchronizer.java
Added new field TypeId to dependency index for exact type searching
[simantics/platform.git] / bundles / org.simantics.structural.synchronization.client / src / org / simantics / structural / synchronization / Synchronizer.java
1 package org.simantics.structural.synchronization;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.List;
7
8 import org.eclipse.core.runtime.IProgressMonitor;
9 import org.simantics.db.ReadGraph;
10 import org.simantics.db.Resource;
11 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
12 import org.simantics.db.exception.CancelTransactionException;
13 import org.simantics.db.exception.DatabaseException;
14 import org.simantics.db.layer0.exception.MissingVariableValueException;
15 import org.simantics.db.layer0.request.ResourceToPossibleVariable;
16 import org.simantics.db.layer0.variable.RVI;
17 import org.simantics.db.layer0.variable.Variable;
18 import org.simantics.db.service.ManagementSupport;
19 import org.simantics.layer0.Layer0;
20 import org.simantics.scl.runtime.SCLContext;
21 import org.simantics.structural.stubs.StructuralResource2;
22 import org.simantics.structural.synchronization.protocol.ChildInfo;
23 import org.simantics.structural.synchronization.protocol.Connection;
24 import org.simantics.structural.synchronization.protocol.SerializedVariable;
25 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
26 import org.simantics.structural.synchronization.protocol.SynchronizationException;
27 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import gnu.trove.map.hash.TObjectIntHashMap;
32 import gnu.trove.set.hash.THashSet;
33
34 public class Synchronizer {
35     
36     private static final Logger LOGGER = LoggerFactory.getLogger(Synchronizer.class);
37     
38     ReadGraph graph;
39     Layer0 L0;
40     StructuralResource2 STR;
41     THashSet<String> visitedTypes = new THashSet<String>();
42     IProgressMonitor monitor;
43     
44     double workDone = 0.0;
45     int workDoneInteger;
46     int maxWork;
47     
48     public Synchronizer(ReadGraph graph) {
49         this.graph = graph;
50         L0 = Layer0.getInstance(graph);
51         STR = StructuralResource2.getInstance(graph);
52     }
53     
54     public void setMonitor(IProgressMonitor monitor, int maxWork) {
55         this.monitor = monitor;
56         this.maxWork = maxWork;
57         this.workDoneInteger = 0;
58     }
59     
60     private void didWork(double amount) {
61         workDone += amount;
62         //System.out.println(workDone);
63         if(monitor != null) {
64             int t = (int)(workDone * maxWork);
65             if(t > workDoneInteger) {
66                 monitor.worked(t-workDoneInteger);
67                 workDoneInteger = t;
68             }
69         }
70     }
71
72     ChildInfo mapChild(Variable child) throws DatabaseException {
73         String name = child.getName(graph);
74         RVI rvi = child.getRVI(graph);
75         return new ChildInfo(name, rvi.toString());
76     }
77
78     Collection<ChildInfo> mapChildren(SynchronizationEventHandler handler, Collection<Variable> children) throws DatabaseException {
79         ArrayList<ChildInfo> result = new ArrayList<ChildInfo>(children.size());
80         for(Variable child : children) {
81             if(child.getPossibleType(graph, STR.Component) != null)
82                 try {
83                     result.add(mapChild(child));
84                 } catch(Exception e) {
85                     handler.reportProblem("Failed to get ChildInfo for " + child + ".", e);
86                 }
87         }
88         return result;
89     }
90
91     Collection<Connection> mapConnections(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
92         ArrayList<Connection> result = new ArrayList<Connection>();
93         for(Variable conn : child.getProperties(graph, StructuralResource2.URIs.SynchronizedConnectionRelation)) {
94             String name = conn.getName(graph);
95             org.simantics.structural2.variables.Connection vc = conn.getValue(graph);
96             Collection<VariableConnectionPointDescriptor> connectionPoints = vc.getConnectionPointDescriptors(graph, null);
97             List<String> cps = new ArrayList<String>(connectionPoints.size());
98             for(VariableConnectionPointDescriptor desc : connectionPoints) {
99                 if(desc.isFlattenedFrom(graph, conn)) continue;
100                 if(!desc.hasClassification(graph, StructuralResource2.URIs.ProvidingConnectionRelation)) continue;
101                 String cpRef = desc.getRelativeRVI(graph, child);
102                 cps.add(cpRef);
103             }
104             
105             result.add(new Connection(name, cps));
106             
107         }
108         
109         return result;
110         
111     }
112     
113     private SerializedVariable serialize(SynchronizationEventHandler handler, Variable var, String name) throws DatabaseException {
114         try {
115                 SerializedVariable result = new SerializedVariable(name, var.getVariantValue(graph));
116                 for(Variable prop : var.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
117                         String pName = prop.getName(graph);
118                         SerializedVariable v = serialize(handler, prop, pName);
119                         if(v != null) result.addProperty(pName, v);
120                 }
121                 return result;
122         } catch (MissingVariableValueException e) {
123             handler.reportProblem("Failed to read " + name + ". " + e.getMessage());
124             
125             Throwable cur = e;
126             while((cur = cur.getCause()) != null) {
127                 if(!(cur instanceof MissingVariableValueException)) {
128                     handler.reportProblem(cur.getMessage());
129                 }
130             }
131         } catch (Exception e) {
132                 handler.reportProblem("Failed to serialize " + name + ": " + e.getMessage(), e);
133         }
134         
135         return null;
136         
137     }
138
139     Collection<SerializedVariable> mapProperties(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
140         ArrayList<SerializedVariable> result = new ArrayList<SerializedVariable>();
141         for(Variable prop : child.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
142                 SerializedVariable serialized = serialize(handler, prop, prop.getName(graph));
143                 if(serialized != null) result.add(serialized);
144         }
145         return result;
146     }
147
148     /**
149      * Assumes that the variable points to a composite.
150      */
151     public void fullSynchronization(Variable variable, SynchronizationEventHandler handler) throws DatabaseException {
152         long duration = 0;
153         if(LOGGER.isTraceEnabled()) {
154             LOGGER.trace("fullSynchronization {}", variable.getURI(graph));
155             duration -= System.nanoTime();
156         }
157         SCLContext context = SCLContext.getCurrent();
158         Object oldGraph = context.put("graph", graph);
159         try {
160             handler.beginSynchronization();
161             synchronizationRec(variable, handler, null, 1.0);
162             handler.endSynchronization();
163         } finally {
164             context.put("graph", oldGraph);
165         }
166         if(LOGGER.isTraceEnabled()) {
167             duration += System.nanoTime();
168             LOGGER.trace("full sync done in {} s", 1e-9*duration);
169         }
170     }
171     
172     /**
173      * Recursive implementation of partial and full synchronization. If {@code changeFlags}
174      * is null, this is full synchronization, otherwise partial synchronization.
175      */
176     private void synchronizationRec(Variable variable, SynchronizationEventHandler handler,
177             TObjectIntHashMap<Variable> changeFlags,
178             double proportionOfWork) throws DatabaseException {
179         String name = variable.getName(graph);
180         Resource type = variable.getPossibleType(graph, STR.Component);
181         if(type == null) {
182             // This same filtering is done separately in mapChildren when beginComponent has been called for the parent.
183             return;
184         }
185         Collection<Variable> children = variable.getChildren(graph);
186         if(graph.isInheritedFrom(type, STR.Composite) || graph.isInheritedFrom(type, STR.AbstractDefinedComponentType)) {
187             String typeId = graph.getPossibleURI(type);
188             if(typeId == null)
189                 throw new SynchronizationException("User component " + type + " does not have an URI.");
190             if(visitedTypes.add(typeId))
191                 visitType(typeId, type, handler);
192             boolean endComponentNeeded = false;
193             try {
194                 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
195                 Collection<ChildInfo> childMap = mapChildren(handler, children);
196                 endComponentNeeded = true;
197                 handler.beginComponent(name, typeId, 
198                         propertyMap,
199                         Collections.<Connection>emptyList(), 
200                         childMap);
201             } catch(Exception e) {
202                 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
203                 if (endComponentNeeded)
204                     handler.endComponent();
205                 return;
206             }
207         } else {
208             String typeId = graph.getPossibleURI(type);
209             if(typeId == null)
210                 throw new SynchronizationException("User component " + type + " does not have an URI.");
211             if(visitedTypes.add(typeId))
212                 visitType(typeId, type, handler);
213             boolean endComponentNeeded = false;
214             try {
215                 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
216                 Collection<Connection> connectionMap = mapConnections(handler, variable);
217                 Collection<ChildInfo> childMap = mapChildren(handler, children);
218                 endComponentNeeded = true;
219                 handler.beginComponent(name, 
220                         typeId,
221                         propertyMap,
222                         connectionMap,
223                         childMap);
224             } catch(Exception e) {
225                 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
226                 if (endComponentNeeded)
227                     handler.endComponent();
228                 return;
229             }
230         }
231         
232         if(changeFlags == null) {
233             // Full synchronization, synchronize all children
234             if(children.size() > 0) {
235                 double proportionOfWorkForChildren = proportionOfWork / children.size();
236                 for(Variable child : children)
237                     synchronizationRec(child, handler, null, proportionOfWorkForChildren);
238             }
239             else {
240                 didWork(proportionOfWork);
241                 // Full synchronization is cancelable
242                 if(monitor != null && monitor.isCanceled())
243                     throw new CancelTransactionException();
244             }
245         }
246         else {
247             // Partial synchronization, synchronize only children with positive changeFlag
248             int relevantChildCount = 0;
249             for(final Variable child : children) {
250                 int changeStatus = changeFlags.get(child);
251                 if(changeStatus != 0)
252                     ++relevantChildCount;
253             }
254             if(relevantChildCount > 0) {
255                 double proportionOfWorkForChildren = proportionOfWork / relevantChildCount;
256                 for(final Variable child : children) {
257                     int changeStatus = changeFlags.get(child);
258                     if(changeStatus == 0)
259                         continue;
260                     synchronizationRec(child, handler,
261                             // Apply full synchronization for subtree if changeStatus > 1
262                             changeStatus==1 ? changeFlags : null,
263                             proportionOfWorkForChildren
264                             );
265                 }
266             }
267             else {
268                 didWork(proportionOfWork);
269             }
270         }
271         handler.endComponent();
272     }
273     
274     public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
275         long duration = 0;
276         if(LOGGER.isTraceEnabled()) {
277             LOGGER.trace("partialSynchronization {}", variable.getURI(graph));
278             duration -= System.nanoTime();
279         }
280         int changeStatus = changeFlags.get(variable);
281         if(changeStatus == 0) return;
282         SCLContext context = SCLContext.getCurrent();
283         Object oldGraph = context.put("graph", graph);
284         try {
285             handler.beginSynchronization();
286             synchronizationRec(variable, handler, changeStatus == 1 ? changeFlags : null, 1.0);
287             handler.endSynchronization();
288         } finally {
289             context.put("graph", oldGraph);
290         } 
291         if(LOGGER.isTraceEnabled()) {
292             duration += System.nanoTime();
293             LOGGER.trace("partial sync in {} s", 1e-9*duration);
294         }
295     }
296     
297     public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, long fromRevision) throws DatabaseException {
298         boolean trace = LOGGER.isTraceEnabled();
299         if (trace)
300             LOGGER.trace("Partial synchronization for {} from rev {}", variable.getURI(graph), fromRevision);
301
302         TObjectIntHashMap<Variable> modifiedComponents = StructuralChangeFlattener.getModifiedComponents(graph, variable, fromRevision);
303
304         if (trace) {
305             LOGGER.trace("----------------");
306             modifiedComponents.forEachEntry((Variable a, int b) -> {
307                 try {
308                     LOGGER.trace("Changed: " + a.getURI(graph) + " " + b);
309                 } catch (DatabaseException e) {
310                     LOGGER.error("Variable.getURI failed", e);
311                 }
312                 return true;
313             });
314         }
315
316         partialSynchronization(variable, handler, modifiedComponents);
317     }
318     
319     void visitType(String typeId, Resource typeResource, SynchronizationEventHandler handler) throws DatabaseException {
320         Variable typeVariable = graph.syncRequest(new ResourceToPossibleVariable(typeResource), TransientCacheAsyncListener.<Variable>instance());
321         handler.beginType(typeId, mapProperties(handler, typeVariable));
322         handler.endType();
323     }
324
325     public long getHeadRevisionId() throws DatabaseException {
326         return graph.getService(ManagementSupport.class).getHeadRevisionId()+1;
327     }
328
329 }