--- /dev/null
+package org.simantics.graph.diff;\r
+\r
+import gnu.trove.map.hash.TIntObjectHashMap;\r
+import gnu.trove.procedure.TIntObjectProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.graph.matching.GraphMatching;\r
+import org.simantics.graph.representation.External;\r
+import org.simantics.graph.representation.Identity;\r
+import org.simantics.graph.representation.IdentityDefinition;\r
+import org.simantics.graph.representation.Internal;\r
+import org.simantics.graph.representation.Optional;\r
+import org.simantics.graph.representation.Root;\r
+import org.simantics.graph.representation.TransferableGraph1;\r
+import org.simantics.graph.representation.Value;\r
+\r
+public class Diff extends GraphMatching {\r
+\r
+ TransferableGraph1 a;\r
+ TransferableGraph1 b;\r
+ \r
+ public Diff(TransferableGraph1 a, TransferableGraph1 b) {\r
+ super(a, b);\r
+ this.a = a;\r
+ this.b = b;\r
+ } \r
+ \r
+ static THashSet<Statement> createStatementSet(int[] statements) {\r
+ THashSet<Statement> result = new THashSet<Statement>();\r
+ for(int i=0;i<statements.length;i+=4)\r
+ result.add(new Statement(\r
+ statements[i],\r
+ statements[i+1],\r
+ statements[i+2],\r
+ statements[i+3]\r
+ ));\r
+ return result;\r
+ }\r
+ \r
+ public TransferableGraphDelta1 diff() {\r
+ match();\r
+ \r
+ long begin = System.nanoTime();\r
+ \r
+ // Statement diff;\r
+ THashSet<Statement> aStatements = createStatementSet(a.statements);\r
+ ArrayList<Statement> bStatements = new ArrayList<Statement>();\r
+ \r
+ int[] statements = b.statements;\r
+ for(int i=0;i<statements.length;i+=4) {\r
+ Statement statement = new Statement(\r
+ statements[i],\r
+ statements[i+1],\r
+ statements[i+2],\r
+ statements[i+3]\r
+ );\r
+ Statement mappedStatement = statement.map(bToA);\r
+ if(mappedStatement == null || !aStatements.remove(mappedStatement))\r
+ bStatements.add(statement);\r
+ } \r
+ \r
+ // Identity diff\r
+ TIntObjectHashMap<IdentityDefinition> aIdentities = new TIntObjectHashMap<IdentityDefinition>();\r
+ ArrayList<Identity> bIdentities = new ArrayList<Identity>();\r
+ for(Identity id : a.identities)\r
+ aIdentities.put(id.resource, id.definition);\r
+ for(Identity id : b.identities) {\r
+ int a = bToA[id.resource];\r
+ IdentityDefinition def = aIdentities.get(a);\r
+ if(def != null && identityDefinitionEquals(bToA, def, id.definition))\r
+ aIdentities.remove(a);\r
+ else\r
+ bIdentities.add(id);\r
+ }\r
+ \r
+ // Value diff\r
+ TIntObjectHashMap<Variant> aValues = new TIntObjectHashMap<Variant>();\r
+ ArrayList<Value> bValues = new ArrayList<Value>();\r
+ for(Value value : a.values)\r
+ aValues.put(value.resource, value.value);\r
+ \r
+ for(Value value : b.values) {\r
+ int a = bToA[value.resource]; // a may be -1\r
+ Variant aValue = aValues.get(a);\r
+ if(aValue != null && aValue.equals(value.value))\r
+ aValues.remove(a);\r
+ else\r
+ bValues.add(value);\r
+ }\r
+ \r
+ // Create result\r
+ TransferableGraphDelta1 result = new TransferableGraphDelta1(\r
+ aToB,\r
+ new TransferableGraph1(\r
+ a.resourceCount,\r
+ toIdentityArray(aIdentities),\r
+ toStatementArray(aStatements),\r
+ toValueArray(aValues)\r
+ ),\r
+ new TransferableGraph1(\r
+ b.resourceCount,\r
+ bIdentities.toArray(new Identity[bIdentities.size()]),\r
+ toStatementArray(bStatements),\r
+ bValues.toArray(new Value[bValues.size()])\r
+ )\r
+ );\r
+ if(GraphMatching.TIMING) {\r
+ long end = System.nanoTime();\r
+ System.out.println("Diffing: " + (end-begin)*1e-6 + "ms");\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ private static boolean identityDefinitionEquals(int[] bToA,\r
+ IdentityDefinition a, IdentityDefinition b) { \r
+ if(a instanceof Root)\r
+ return b instanceof Root && ((Root)a).name.equals(((Root)b).name);\r
+ if(b instanceof Root)\r
+ return false;\r
+ int aParent, bParent;\r
+ String aName, bName;\r
+ \r
+ if(b instanceof External) {\r
+ External def = (External)b;\r
+ bParent = def.parent;\r
+ bName = def.name;\r
+ }\r
+ else if(b instanceof Internal) {\r
+ Internal def = (Internal)b;\r
+ bParent = def.parent;\r
+ bName = def.name;\r
+ }\r
+ else if(b instanceof Optional) {\r
+ Optional def = (Optional)b;\r
+ bParent = def.parent;\r
+ bName = def.name;\r
+ }\r
+ else\r
+ return false;\r
+ bParent = bToA[bParent];\r
+ if(bParent < 0)\r
+ return false;\r
+ \r
+ if(a instanceof External) {\r
+ External def = (External)a;\r
+ aParent = def.parent;\r
+ aName = def.name;\r
+ }\r
+ else if(a instanceof Internal) {\r
+ Internal def = (Internal)a;\r
+ aParent = def.parent;\r
+ aName = def.name;\r
+ }\r
+ else if(a instanceof Optional) {\r
+ Optional def = (Optional)a;\r
+ aParent = def.parent;\r
+ aName = def.name;\r
+ }\r
+ else\r
+ return false;\r
+ return aParent == bParent && aName.equals(bName);\r
+ }\r
+\r
+ static int[] toStatementArray(Collection<Statement> statements) {\r
+ int[] result = new int[4*statements.size()];\r
+ int i=0;\r
+ for(Statement statement : statements) {\r
+ result[i++] = statement.subject;\r
+ result[i++] = statement.predicate;\r
+ result[i++] = statement.inverse;\r
+ result[i++] = statement.object;\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ static Value[] toValueArray(TIntObjectHashMap<Variant> values) {\r
+ final Value[] result = new Value[values.size()];\r
+ values.forEachEntry(new TIntObjectProcedure<Variant>() {\r
+ int i=0;\r
+ @Override\r
+ public boolean execute(int a, Variant b) {\r
+ result[i++] = new Value(a, b);\r
+ return true;\r
+ }\r
+ });\r
+ return result;\r
+ }\r
+ \r
+ static Identity[] toIdentityArray(TIntObjectHashMap<IdentityDefinition> identities) {\r
+ final Identity[] result = new Identity[identities.size()];\r
+ identities.forEachEntry(new TIntObjectProcedure<IdentityDefinition>() {\r
+ int i=0;\r
+ @Override\r
+ public boolean execute(int a, IdentityDefinition b) {\r
+ result[i++] = new Identity(a, b);\r
+ return true;\r
+ }\r
+ });\r
+ return result;\r
+ }\r
+ \r
+}
\ No newline at end of file