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