--- /dev/null
+package org.simantics.structural.synchronization;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Map;\r
+\r
+import org.simantics.databoard.Bindings;\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.genericrelation.DependencyChanges;\r
+import org.simantics.db.layer0.request.ModelInstances;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.structural2.utils.StructuralChangeVisitor;\r
+import org.simantics.structural2.utils.StructuralMetadataUtils;\r
+\r
+/**\r
+ * Browses change metadata from the requested revision and\r
+ * marks the components in a flat structural configuration\r
+ * where changes have been occured.\r
+ * \r
+ * @author Hannu Niemistö\r
+ */\r
+public class StructuralChangeFlattener {\r
+ \r
+ private static Variable[] EMPTY_VARIABLE_ARRAY = new Variable[0];\r
+ \r
+ public static TObjectIntHashMap<Variable> getModifiedComponents(ReadGraph graph,\r
+ Variable rootVariable, long fromRevision) throws DatabaseException {\r
+ Visitor visitor = new Visitor(graph, rootVariable);\r
+ StructuralMetadataUtils.visitStructuralChangesFrom(graph,\r
+ Variables.getModel(graph, rootVariable),\r
+ fromRevision, visitor);\r
+ return visitor.modifiedComponents;\r
+ }\r
+\r
+ public static TObjectIntHashMap<Variable> getModifiedComponents(ReadGraph graph,\r
+ Variable rootVariable, DependencyChanges.Change[] changes) throws DatabaseException {\r
+ Visitor visitor = new Visitor(graph, rootVariable);\r
+ StructuralMetadataUtils.visitStructuralChangesFrom(graph,\r
+ Variables.getModel(graph, rootVariable),\r
+ changes, visitor);\r
+ return visitor.modifiedComponents;\r
+ }\r
+\r
+ private static class Visitor implements StructuralChangeVisitor {\r
+\r
+ Variable rootVariable;\r
+ Resource rootResource;\r
+ Resource model;\r
+ THashMap<Resource, Variable[]> visitedVariables = new THashMap<Resource, Variable[]>();\r
+ Layer0 L0;\r
+ StructuralResource2 STR;\r
+ TObjectIntHashMap<Variable> modifiedComponents = new TObjectIntHashMap<Variable>(); \r
+ \r
+ public Visitor(ReadGraph graph, Variable rootVariable) throws DatabaseException {\r
+ this.rootVariable = rootVariable;\r
+ this.rootResource = rootVariable.getRepresents(graph);\r
+ this.model = Variables.getModel(graph, rootVariable);\r
+ this.L0 = Layer0.getInstance(graph);\r
+ this.STR = StructuralResource2.getInstance(graph);\r
+ }\r
+ \r
+ @Override\r
+ public void visitComponentChange(ReadGraph graph, Resource component, boolean componentItselfModified)\r
+ throws DatabaseException {\r
+ Variable[] variables = visitedVariables.get(component);\r
+ if(variables == null) {\r
+ variables = visitUnseenComponent(graph, component);\r
+ visitedVariables.put(component, variables);\r
+ }\r
+ if(componentItselfModified)\r
+ for(Variable variable : variables)\r
+ modifiedComponents.put(variable, 2);\r
+ else\r
+ for(Variable variable : variables)\r
+ modifiedComponents.putIfAbsent(variable, 1);\r
+ }\r
+ \r
+ private Variable[] visitUnseenComponent(ReadGraph graph, Resource component) throws DatabaseException {\r
+ // Is root?\r
+ if(component.equals(rootResource))\r
+ return new Variable[] { rootVariable };\r
+ \r
+ // Check if this a component type root\r
+ Resource componentType = graph.getPossibleObject(component, STR.Defines);\r
+ if(componentType != null) {\r
+ ArrayList<Variable> result = new ArrayList<Variable>();\r
+ Map<String,Resource> instances = graph.syncRequest(new ModelInstances(model, componentType), TransientCacheAsyncListener.<Map<String,Resource>>instance());\r
+ for(Resource instance : instances.values()) {\r
+ visitComponentChange(graph, instance, false);\r
+ for(Variable variable : visitedVariables.get(instance))\r
+ result.add(variable);\r
+ }\r
+ if(result.isEmpty())\r
+ return EMPTY_VARIABLE_ARRAY;\r
+ else\r
+ return result.toArray(new Variable[result.size()]);\r
+ }\r
+ \r
+ // Check parent\r
+ Resource parent = graph.getPossibleObject(component, L0.PartOf);\r
+ String name = graph.getPossibleRelatedValue(component, L0.HasName, Bindings.STRING);\r
+ if(parent == null || name == null || !graph.isInstanceOf(component, STR.Component))\r
+ return EMPTY_VARIABLE_ARRAY;\r
+ \r
+ visitComponentChange(graph, parent, false);\r
+ Variable[] parentCorrespondences = visitedVariables.get(parent);\r
+ ArrayList<Variable> result = new ArrayList<Variable>(parentCorrespondences.length);\r
+ for(Variable parentCorrespondence : parentCorrespondences) {\r
+ Variable correspondence = parentCorrespondence.getPossibleChild(graph, name);\r
+ if(correspondence != null)\r
+ result.add(correspondence);\r
+ }\r
+ if(result.isEmpty())\r
+ return EMPTY_VARIABLE_ARRAY;\r
+ else\r
+ return result.toArray(new Variable[result.size()]);\r
+ }\r
+ \r
+ }\r
+}\r