]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/Synchronizer.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.structural.synchronization.client / src / org / simantics / structural / synchronization / Synchronizer.java
index d2738d84444fab9c02739555a676f1bf156d26ee..9c7428416531a2da02c33d9ac10c895736a4797c 100644 (file)
-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.CancelTransactionException;\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 children\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
-                // Full synchronization is cancelable\r
-                if(monitor != null && monitor.isCanceled())\r
-                    throw new CancelTransactionException();\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
+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;
+    }
+
+    
+}