1 package org.simantics.structural.synchronization;
\r
3 import gnu.trove.map.hash.TObjectIntHashMap;
\r
4 import gnu.trove.set.hash.THashSet;
\r
6 import java.util.ArrayList;
\r
7 import java.util.Collection;
\r
8 import java.util.Collections;
\r
9 import java.util.List;
\r
11 import org.eclipse.core.runtime.IProgressMonitor;
\r
12 import org.simantics.db.ReadGraph;
\r
13 import org.simantics.db.Resource;
\r
14 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
\r
15 import org.simantics.db.exception.DatabaseException;
\r
16 import org.simantics.db.layer0.exception.MissingVariableValueException;
\r
17 import org.simantics.db.layer0.request.ResourceToPossibleVariable;
\r
18 import org.simantics.db.layer0.variable.RVI;
\r
19 import org.simantics.db.layer0.variable.Variable;
\r
20 import org.simantics.db.service.ManagementSupport;
\r
21 import org.simantics.layer0.Layer0;
\r
22 import org.simantics.scl.runtime.SCLContext;
\r
23 import org.simantics.structural.stubs.StructuralResource2;
\r
24 import org.simantics.structural.synchronization.protocol.ChildInfo;
\r
25 import org.simantics.structural.synchronization.protocol.Connection;
\r
26 import org.simantics.structural.synchronization.protocol.SerializedVariable;
\r
27 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
\r
28 import org.simantics.structural.synchronization.protocol.SynchronizationException;
\r
29 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
\r
31 public class Synchronizer {
\r
33 public static boolean TRACE = false;
\r
37 StructuralResource2 STR;
\r
38 THashSet<String> visitedTypes = new THashSet<String>();
\r
39 IProgressMonitor monitor;
\r
41 double workDone = 0.0;
\r
42 int workDoneInteger;
\r
45 public Synchronizer(ReadGraph graph) {
\r
47 L0 = Layer0.getInstance(graph);
\r
48 STR = StructuralResource2.getInstance(graph);
\r
51 public void setMonitor(IProgressMonitor monitor, int maxWork) {
\r
52 this.monitor = monitor;
\r
53 this.maxWork = maxWork;
\r
54 this.workDoneInteger = 0;
\r
57 private void didWork(double amount) {
\r
59 //System.out.println(workDone);
\r
60 if(monitor != null) {
\r
61 int t = (int)(workDone * maxWork);
\r
62 if(t > workDoneInteger) {
\r
63 monitor.worked(t-workDoneInteger);
\r
64 workDoneInteger = t;
\r
69 ChildInfo mapChild(Variable child) throws DatabaseException {
\r
70 String name = child.getName(graph);
\r
71 RVI rvi = child.getRVI(graph);
\r
72 return new ChildInfo(name, rvi.toString());
\r
75 Collection<ChildInfo> mapChildren(SynchronizationEventHandler handler, Collection<Variable> children) throws DatabaseException {
\r
76 ArrayList<ChildInfo> result = new ArrayList<ChildInfo>(children.size());
\r
77 for(Variable child : children) {
\r
78 if(child.getPossibleType(graph, STR.Component) != null)
\r
80 result.add(mapChild(child));
\r
81 } catch(Exception e) {
\r
82 handler.reportProblem("Failed to get ChildInfo for " + child + ".", e);
\r
88 Collection<Connection> mapConnections(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
\r
89 ArrayList<Connection> result = new ArrayList<Connection>();
\r
90 for(Variable conn : child.getProperties(graph, StructuralResource2.URIs.SynchronizedConnectionRelation)) {
\r
91 String name = conn.getName(graph);
\r
92 org.simantics.structural2.variables.Connection vc = conn.getValue(graph);
\r
93 Collection<VariableConnectionPointDescriptor> connectionPoints = vc.getConnectionPointDescriptors(graph, null);
\r
94 List<String> cps = new ArrayList<String>(connectionPoints.size());
\r
95 for(VariableConnectionPointDescriptor desc : connectionPoints) {
\r
96 if(desc.isFlattenedFrom(graph, conn)) continue;
\r
97 if(!desc.hasClassification(graph, StructuralResource2.URIs.ProvidingConnectionRelation)) continue;
\r
98 String cpRef = desc.getRelativeRVI(graph, child);
\r
102 result.add(new Connection(name, cps));
\r
110 private SerializedVariable serialize(SynchronizationEventHandler handler, Variable var, String name) throws DatabaseException {
\r
112 SerializedVariable result = new SerializedVariable(name, var.getVariantValue(graph));
\r
113 for(Variable prop : var.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
\r
114 String pName = prop.getName(graph);
\r
115 SerializedVariable v = serialize(handler, prop, pName);
\r
116 if(v != null) result.addProperty(pName, v);
\r
119 } catch (MissingVariableValueException e) {
\r
120 handler.reportProblem("Failed to read " + name + ". " + e.getMessage());
\r
123 while((cur = cur.getCause()) != null) {
\r
124 if(!(cur instanceof MissingVariableValueException)) {
\r
125 handler.reportProblem(cur.getMessage());
\r
129 } catch (Exception e) {
\r
130 handler.reportProblem("Failed to serialize " + name + ": " + e.getMessage(), e);
\r
137 Collection<SerializedVariable> mapProperties(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
\r
138 ArrayList<SerializedVariable> result = new ArrayList<SerializedVariable>();
\r
139 for(Variable prop : child.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
\r
140 SerializedVariable serialized = serialize(handler, prop, prop.getName(graph));
\r
141 if(serialized != null) result.add(serialized);
\r
147 * Assumes that the variable points to a composite.
\r
149 public void fullSynchronization(Variable variable, SynchronizationEventHandler handler) throws DatabaseException {
\r
152 System.out.println("fullSynchronization " + variable.getURI(graph));
\r
153 duration -= System.nanoTime();
\r
155 SCLContext context = SCLContext.getCurrent();
\r
156 Object oldGraph = context.put("graph", graph);
\r
158 handler.beginSynchronization();
\r
159 synchronizationRec(variable, handler, null, 1.0);
\r
160 handler.endSynchronization();
\r
162 context.put("graph", oldGraph);
\r
165 duration += System.nanoTime();
\r
166 System.out.println("full sync in " + 1e-9*duration + "s.");
\r
171 * Recursive implementation of partial and full synchronization. If {@code changeFlags}
\r
172 * is null, this is full synchronization, otherwise partial synchronization.
\r
174 private void synchronizationRec(Variable variable, SynchronizationEventHandler handler,
\r
175 TObjectIntHashMap<Variable> changeFlags,
\r
176 double proportionOfWork) throws DatabaseException {
\r
177 String name = variable.getName(graph);
\r
178 Resource type = variable.getPossibleType(graph, STR.Component);
\r
180 // This same filtering is done separately in mapChildren when beginComponent has been called for the parent.
\r
183 Collection<Variable> children = variable.getChildren(graph);
\r
184 if(graph.isInheritedFrom(type, STR.Composite) || graph.isInheritedFrom(type, STR.AbstractDefinedComponentType)) {
\r
185 String typeId = graph.getPossibleURI(type);
\r
187 throw new SynchronizationException("User component " + type + " does not have an URI.");
\r
188 if(visitedTypes.add(typeId))
\r
189 visitType(typeId, type, handler);
\r
190 boolean endComponentNeeded = false;
\r
192 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
\r
193 Collection<ChildInfo> childMap = mapChildren(handler, children);
\r
194 endComponentNeeded = true;
\r
195 handler.beginComponent(name, typeId,
\r
197 Collections.<Connection>emptyList(),
\r
199 } catch(Exception e) {
\r
200 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
\r
201 if (endComponentNeeded)
\r
202 handler.endComponent();
\r
206 String typeId = graph.getPossibleURI(type);
\r
208 throw new SynchronizationException("User component " + type + " does not have an URI.");
\r
209 if(visitedTypes.add(typeId))
\r
210 visitType(typeId, type, handler);
\r
211 boolean endComponentNeeded = false;
\r
213 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
\r
214 Collection<Connection> connectionMap = mapConnections(handler, variable);
\r
215 Collection<ChildInfo> childMap = mapChildren(handler, children);
\r
216 endComponentNeeded = true;
\r
217 handler.beginComponent(name,
\r
222 } catch(Exception e) {
\r
223 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
\r
224 if (endComponentNeeded)
\r
225 handler.endComponent();
\r
230 if(changeFlags == null) {
\r
231 // Full synchronization, synchronize all childre
\r
232 if(children.size() > 0) {
\r
233 double proportionOfWorkForChildren = proportionOfWork / children.size();
\r
234 for(Variable child : children)
\r
235 synchronizationRec(child, handler, null, proportionOfWorkForChildren);
\r
238 didWork(proportionOfWork);
\r
242 // Partial synchronization, synchronize only children with positive changeFlag
\r
243 int relevantChildCount = 0;
\r
244 for(final Variable child : children) {
\r
245 int changeStatus = changeFlags.get(child);
\r
246 if(changeStatus != 0)
\r
247 ++relevantChildCount;
\r
249 if(relevantChildCount > 0) {
\r
250 double proportionOfWorkForChildren = proportionOfWork / relevantChildCount;
\r
251 for(final Variable child : children) {
\r
252 int changeStatus = changeFlags.get(child);
\r
253 if(changeStatus == 0)
\r
255 synchronizationRec(child, handler,
\r
256 // Apply full synchronization for subtree if changeStatus > 1
\r
257 changeStatus==1 ? changeFlags : null,
\r
258 proportionOfWorkForChildren
\r
263 didWork(proportionOfWork);
\r
266 handler.endComponent();
\r
269 public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
\r
272 System.out.println("partialSynchronization " + variable.getURI(graph));
\r
273 duration -= System.nanoTime();
\r
275 int changeStatus = changeFlags.get(variable);
\r
276 if(changeStatus == 0) return;
\r
277 SCLContext context = SCLContext.getCurrent();
\r
278 Object oldGraph = context.put("graph", graph);
\r
280 handler.beginSynchronization();
\r
281 synchronizationRec(variable, handler, changeStatus == 1 ? changeFlags : null, 1.0);
\r
282 handler.endSynchronization();
\r
284 context.put("graph", oldGraph);
\r
287 duration += System.nanoTime();
\r
288 System.out.println("partial sync in " + 1e-9*duration + "s.");
\r
292 public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, long fromRevision) throws DatabaseException {
\r
293 TObjectIntHashMap<Variable> modifiedComponents = StructuralChangeFlattener.getModifiedComponents(graph, variable, fromRevision);
\r
294 /*System.out.println("----------------");
\r
295 modifiedComponents.forEachEntry(
\r
296 new TObjectIntProcedure<Variable>() {
\r
298 public boolean execute(Variable a, int b) {
\r
300 System.out.println("Changed: " + a.getURI(graph) + " " + b);
\r
301 } catch (DatabaseException e) {
\r
302 e.printStackTrace();
\r
307 partialSynchronization(variable, handler, modifiedComponents);
\r
310 void visitType(String typeId, Resource typeResource, SynchronizationEventHandler handler) throws DatabaseException {
\r
311 Variable typeVariable = graph.syncRequest(new ResourceToPossibleVariable(typeResource), TransientCacheAsyncListener.<Variable>instance());
\r
312 handler.beginType(typeId, mapProperties(handler, typeVariable));
\r
316 public long getHeadRevisionId() throws DatabaseException {
\r
317 return graph.getService(ManagementSupport.class).getHeadRevisionId()+1;
\r