-package org.simantics.structural.synchronization;
-
-import gnu.trove.map.hash.TObjectIntHashMap;
-import gnu.trove.set.hash.THashSet;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.simantics.db.ReadGraph;
-import org.simantics.db.Resource;
-import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
-import org.simantics.db.exception.CancelTransactionException;
-import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.layer0.exception.MissingVariableValueException;
-import org.simantics.db.layer0.request.ResourceToPossibleVariable;
-import org.simantics.db.layer0.variable.RVI;
-import org.simantics.db.layer0.variable.Variable;
-import org.simantics.db.service.ManagementSupport;
-import org.simantics.layer0.Layer0;
-import org.simantics.scl.runtime.SCLContext;
-import org.simantics.structural.stubs.StructuralResource2;
-import org.simantics.structural.synchronization.protocol.ChildInfo;
-import org.simantics.structural.synchronization.protocol.Connection;
-import org.simantics.structural.synchronization.protocol.SerializedVariable;
-import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
-import org.simantics.structural.synchronization.protocol.SynchronizationException;
-import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
-
-public class Synchronizer {
-
- public static boolean TRACE = false;
-
- ReadGraph graph;
- Layer0 L0;
- StructuralResource2 STR;
- THashSet<String> visitedTypes = new THashSet<String>();
- IProgressMonitor monitor;
-
- double workDone = 0.0;
- int workDoneInteger;
- int maxWork;
-
- public Synchronizer(ReadGraph graph) {
- this.graph = graph;
- L0 = Layer0.getInstance(graph);
- STR = StructuralResource2.getInstance(graph);
- }
-
- public void setMonitor(IProgressMonitor monitor, int maxWork) {
- this.monitor = monitor;
- this.maxWork = maxWork;
- this.workDoneInteger = 0;
- }
-
- private void didWork(double amount) {
- workDone += amount;
- //System.out.println(workDone);
- if(monitor != null) {
- int t = (int)(workDone * maxWork);
- if(t > workDoneInteger) {
- monitor.worked(t-workDoneInteger);
- workDoneInteger = t;
- }
- }
- }
-
- ChildInfo mapChild(Variable child) throws DatabaseException {
- String name = child.getName(graph);
- RVI rvi = child.getRVI(graph);
- return new ChildInfo(name, rvi.toString());
- }
-
- Collection<ChildInfo> mapChildren(SynchronizationEventHandler handler, Collection<Variable> children) throws DatabaseException {
- ArrayList<ChildInfo> result = new ArrayList<ChildInfo>(children.size());
- for(Variable child : children) {
- if(child.getPossibleType(graph, STR.Component) != null)
- try {
- result.add(mapChild(child));
- } catch(Exception e) {
- handler.reportProblem("Failed to get ChildInfo for " + child + ".", e);
- }
- }
- return result;
- }
-
- Collection<Connection> mapConnections(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
- ArrayList<Connection> result = new ArrayList<Connection>();
- for(Variable conn : child.getProperties(graph, StructuralResource2.URIs.SynchronizedConnectionRelation)) {
- String name = conn.getName(graph);
- org.simantics.structural2.variables.Connection vc = conn.getValue(graph);
- Collection<VariableConnectionPointDescriptor> connectionPoints = vc.getConnectionPointDescriptors(graph, null);
- List<String> cps = new ArrayList<String>(connectionPoints.size());
- for(VariableConnectionPointDescriptor desc : connectionPoints) {
- if(desc.isFlattenedFrom(graph, conn)) continue;
- if(!desc.hasClassification(graph, StructuralResource2.URIs.ProvidingConnectionRelation)) continue;
- String cpRef = desc.getRelativeRVI(graph, child);
- cps.add(cpRef);
- }
-
- result.add(new Connection(name, cps));
-
- }
-
- return result;
-
- }
-
- private SerializedVariable serialize(SynchronizationEventHandler handler, Variable var, String name) throws DatabaseException {
- try {
- SerializedVariable result = new SerializedVariable(name, var.getVariantValue(graph));
- for(Variable prop : var.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
- String pName = prop.getName(graph);
- SerializedVariable v = serialize(handler, prop, pName);
- if(v != null) result.addProperty(pName, v);
- }
- return result;
- } catch (MissingVariableValueException e) {
- handler.reportProblem("Failed to read " + name + ". " + e.getMessage());
-
- Throwable cur = e;
- while((cur = cur.getCause()) != null) {
- if(!(cur instanceof MissingVariableValueException)) {
- handler.reportProblem(cur.getMessage());
- break;
- }
- }
- } catch (Exception e) {
- handler.reportProblem("Failed to serialize " + name + ": " + e.getMessage(), e);
- }
-
- return null;
-
- }
-
- Collection<SerializedVariable> mapProperties(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
- ArrayList<SerializedVariable> result = new ArrayList<SerializedVariable>();
- for(Variable prop : child.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
- SerializedVariable serialized = serialize(handler, prop, prop.getName(graph));
- if(serialized != null) result.add(serialized);
- }
- return result;
- }
-
- /**
- * Assumes that the variable points to a composite.
- */
- public void fullSynchronization(Variable variable, SynchronizationEventHandler handler) throws DatabaseException {
- long duration = 0;
- if(TRACE) {
- System.out.println("fullSynchronization " + variable.getURI(graph));
- duration -= System.nanoTime();
- }
- SCLContext context = SCLContext.getCurrent();
- Object oldGraph = context.put("graph", graph);
- try {
- handler.beginSynchronization();
- synchronizationRec(variable, handler, null, 1.0);
- handler.endSynchronization();
- } finally {
- context.put("graph", oldGraph);
- }
- if(TRACE) {
- duration += System.nanoTime();
- System.out.println("full sync in " + 1e-9*duration + "s.");
- }
- }
-
- /**
- * Recursive implementation of partial and full synchronization. If {@code changeFlags}
- * is null, this is full synchronization, otherwise partial synchronization.
- */
- private void synchronizationRec(Variable variable, SynchronizationEventHandler handler,
- TObjectIntHashMap<Variable> changeFlags,
- double proportionOfWork) throws DatabaseException {
- String name = variable.getName(graph);
- Resource type = variable.getPossibleType(graph, STR.Component);
- if(type == null) {
- // This same filtering is done separately in mapChildren when beginComponent has been called for the parent.
- return;
- }
- Collection<Variable> children = variable.getChildren(graph);
- if(graph.isInheritedFrom(type, STR.Composite) || graph.isInheritedFrom(type, STR.AbstractDefinedComponentType)) {
- String typeId = graph.getPossibleURI(type);
- if(typeId == null)
- throw new SynchronizationException("User component " + type + " does not have an URI.");
- if(visitedTypes.add(typeId))
- visitType(typeId, type, handler);
- boolean endComponentNeeded = false;
- try {
- Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
- Collection<ChildInfo> childMap = mapChildren(handler, children);
- endComponentNeeded = true;
- handler.beginComponent(name, typeId,
- propertyMap,
- Collections.<Connection>emptyList(),
- childMap);
- } catch(Exception e) {
- handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
- if (endComponentNeeded)
- handler.endComponent();
- return;
- }
- } else {
- String typeId = graph.getPossibleURI(type);
- if(typeId == null)
- throw new SynchronizationException("User component " + type + " does not have an URI.");
- if(visitedTypes.add(typeId))
- visitType(typeId, type, handler);
- boolean endComponentNeeded = false;
- try {
- Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
- Collection<Connection> connectionMap = mapConnections(handler, variable);
- Collection<ChildInfo> childMap = mapChildren(handler, children);
- endComponentNeeded = true;
- handler.beginComponent(name,
- typeId,
- propertyMap,
- connectionMap,
- childMap);
- } catch(Exception e) {
- handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
- if (endComponentNeeded)
- handler.endComponent();
- return;
- }
- }
-
- if(changeFlags == null) {
- // Full synchronization, synchronize all children
- if(children.size() > 0) {
- double proportionOfWorkForChildren = proportionOfWork / children.size();
- for(Variable child : children)
- synchronizationRec(child, handler, null, proportionOfWorkForChildren);
- }
- else {
- didWork(proportionOfWork);
- // Full synchronization is cancelable
- if(monitor != null && monitor.isCanceled())
- throw new CancelTransactionException();
- }
- }
- else {
- // Partial synchronization, synchronize only children with positive changeFlag
- int relevantChildCount = 0;
- for(final Variable child : children) {
- int changeStatus = changeFlags.get(child);
- if(changeStatus != 0)
- ++relevantChildCount;
- }
- if(relevantChildCount > 0) {
- double proportionOfWorkForChildren = proportionOfWork / relevantChildCount;
- for(final Variable child : children) {
- int changeStatus = changeFlags.get(child);
- if(changeStatus == 0)
- continue;
- synchronizationRec(child, handler,
- // Apply full synchronization for subtree if changeStatus > 1
- changeStatus==1 ? changeFlags : null,
- proportionOfWorkForChildren
- );
- }
- }
- else {
- didWork(proportionOfWork);
- }
- }
- handler.endComponent();
- }
-
- public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
- long duration = 0;
- if(TRACE) {
- System.out.println("partialSynchronization " + variable.getURI(graph));
- duration -= System.nanoTime();
- }
- int changeStatus = changeFlags.get(variable);
- if(changeStatus == 0) return;
- SCLContext context = SCLContext.getCurrent();
- Object oldGraph = context.put("graph", graph);
- try {
- handler.beginSynchronization();
- synchronizationRec(variable, handler, changeStatus == 1 ? changeFlags : null, 1.0);
- handler.endSynchronization();
- } finally {
- context.put("graph", oldGraph);
- }
- if(TRACE) {
- duration += System.nanoTime();
- System.out.println("partial sync in " + 1e-9*duration + "s.");
- }
- }
-
- public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, long fromRevision) throws DatabaseException {
- TObjectIntHashMap<Variable> modifiedComponents = StructuralChangeFlattener.getModifiedComponents(graph, variable, fromRevision);
- /*System.out.println("----------------");
- modifiedComponents.forEachEntry(
- new TObjectIntProcedure<Variable>() {
- @Override
- public boolean execute(Variable a, int b) {
- try {
- System.out.println("Changed: " + a.getURI(graph) + " " + b);
- } catch (DatabaseException e) {
- e.printStackTrace();
- }
- return true;
- }
- });*/
- partialSynchronization(variable, handler, modifiedComponents);
- }
-
- void visitType(String typeId, Resource typeResource, SynchronizationEventHandler handler) throws DatabaseException {
- Variable typeVariable = graph.syncRequest(new ResourceToPossibleVariable(typeResource), TransientCacheAsyncListener.<Variable>instance());
- handler.beginType(typeId, mapProperties(handler, typeVariable));
- handler.endType();
- }
-
- public long getHeadRevisionId() throws DatabaseException {
- return graph.getService(ManagementSupport.class).getHeadRevisionId()+1;
- }
-
-
-}