-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Foster Wheeler Energia Oy - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.interop.test;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.Stack;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.common.request.ReadRequest;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.utils.datastructures.BijectionMap;\r
-import org.simantics.utils.datastructures.MapList;\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-/**\r
- * Compares two subgraphs and reports differences.\r
- * \r
- * Assumes that subgraphs (defined using traverse relations) are not cyclic.\r
- * \r
- * Assumes that properties can be used to identify objects, if relation type is not enough.\r
- * \r
- * \r
- * \r
- * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
- *\r
- */\r
-public class GraphComparator {\r
- \r
- private static final boolean DEBUG = false;\r
-\r
- private Resource r1;\r
- private Resource r2;\r
- private Set<Resource> strong = new HashSet<Resource>(); // List of relations that identify object, if subject is already identified. \r
- private List<Resource> traversed = new ArrayList<Resource>(); // list of relations that are traversed (and tested)\r
- private List<Resource> tested = new ArrayList<Resource>(); // list of relations that are tested, but not traversed\r
- private List<Resource> nonTraversed = new ArrayList<Resource>(); // list of relations that are not traversed\r
- private List<Resource> nonTested = new ArrayList<Resource>(); // list of relations that are not tested\r
- \r
- private List<Statement> changes1 = new ArrayList<Statement>();\r
- private List<Statement> changes2 = new ArrayList<Statement>();\r
- private List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();\r
- private Set<Statement> changes1Set = new HashSet<Statement>();\r
- private Set<Statement> changes2Set = new HashSet<Statement>();\r
- private Set<Pair<Statement,Statement>> modificationsSet = new HashSet<Pair<Statement,Statement>>();\r
-\r
- private BijectionMap<Statement, Statement> comparableStatements = new BijectionMap<Statement, Statement>();\r
- private BijectionMap<Resource, Resource> comparableResources = new BijectionMap<Resource, Resource>();\r
- private Set<Resource> processedResources = new HashSet<Resource>();\r
- \r
- private ResourceComparator comparator;\r
- \r
- private Comparator<Statement> scomp = new PredicateComparator();\r
- private Comparator<Resource> rcomp = new ResComparator();\r
- \r
- // runtime attributes\r
- \r
- private ReadGraph g;\r
- private Layer0 b;\r
- \r
- public GraphComparator(Resource r1, Resource r2) {\r
- this.r1 = r1;\r
- this.r2 = r2;\r
- comparator = new TypeComparator();\r
- }\r
- \r
- public GraphComparator(Resource r1, Resource r2, ResourceComparator comparator) {\r
- this.r1 = r1;\r
- this.r2 = r2;\r
- this.comparator = comparator;\r
- }\r
- \r
- ArrayList<Statement> ss1 = new ArrayList<Statement>();\r
- ArrayList<Statement> ss2 = new ArrayList<Statement>();\r
- \r
- \r
- public Comparator<Resource> getResourceComparator() {\r
- return rcomp;\r
- }\r
- \r
- public Comparator<Statement> getStatementComparator() {\r
- return scomp;\r
- }\r
- \r
- public Resource getR1() {\r
- return r1;\r
- }\r
- \r
- public Resource getR2() {\r
- return r2;\r
- }\r
- \r
- public void addTraversed(Resource rel) {\r
- traversed.add(rel);\r
- }\r
- \r
- public void addTraversed(Collection<Resource> rels) {\r
- traversed.addAll(rels);\r
- }\r
- \r
- public void addNonTraversed(Resource rel) {\r
- nonTraversed.add(rel);\r
- }\r
- \r
- public void addNonTraversed(Collection<Resource> rels) {\r
- nonTraversed.addAll(rels);\r
- }\r
- \r
- public void addTested(Resource rel) {\r
- tested.add(rel);\r
- }\r
- \r
- public void addTested(Collection<Resource> rels) {\r
- tested.addAll(rels);\r
- }\r
- \r
- public void addNonTested(Resource rel) {\r
- nonTested.add(rel);\r
- }\r
- \r
- public void addNonTested(Collection<Resource> rels) {\r
- nonTested.addAll(rels);\r
- }\r
- \r
- public void addComparableResources(Resource r1, Resource r2) {\r
- comparableResources.map(r1, r2);\r
- }\r
- \r
- public void addComparableResources(BijectionMap<Resource, Resource> matching) {\r
- comparableResources.addAll(matching);\r
- }\r
- \r
- public void addStrong(Resource r) {\r
- strong.add(r);\r
- }\r
- \r
- public void addStrong(Collection<Resource> rels) {\r
- strong.addAll(rels);\r
- }\r
- \r
- \r
- public void test(ReadGraph g) throws DatabaseException {\r
- this.g = g;\r
- this.b = Layer0.getInstance(g);\r
- comparator.setComparator(this);\r
- \r
- Stack<Resource> objectsLeft = new Stack<Resource>();\r
- Stack<Resource> objectsRight = new Stack<Resource>();\r
- objectsLeft.push(r1);\r
- objectsRight.push(r2);\r
- \r
- Set<Statement> unreliableLeft = new HashSet<Statement>();\r
- Set<Statement> unreliableRight = new HashSet<Statement>();\r
- \r
- while (true) {\r
- if (objectsLeft.isEmpty())\r
- break;\r
- \r
- \r
- // process compares objects that are identified and searches for more resources to process. \r
- process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);\r
- // process unreliable handles cases where unidentified statements subject and object have been identified \r
- processUnreliable(unreliableLeft, unreliableRight);\r
- // process unreliable handles cases where unidentified resources have path of length one to identified resource\r
- processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);\r
- if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {\r
- // comparison is ending, but we have still unprocessed unidentified resources left.\r
- // These cases have longer path than one to identified objects.\r
- processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);\r
- }\r
- \r
- }\r
- for (Statement s : unreliableLeft) {\r
- if (!comparableStatements.containsLeft(s))\r
- addDeletion(s);\r
- }\r
- for (Statement s : unreliableRight) {\r
- if (!comparableStatements.containsRight(s))\r
- addAddition(s);\r
- }\r
- \r
- \r
- }\r
- \r
- public void test(Session session) throws DatabaseException {\r
- test(session, r1, r2);\r
- }\r
- \r
- public void test(Session session, Resource r1, Resource r2) throws DatabaseException {\r
- \r
- comparator.setComparator(this);\r
- \r
- addComparable(r1, r2, false);\r
- \r
- final Stack<Resource> objectsLeft = new Stack<Resource>();\r
- final Stack<Resource> objectsRight = new Stack<Resource>();\r
- objectsLeft.push(r1);\r
- objectsRight.push(r2);\r
- \r
- final Set<Statement> unreliableLeft = new HashSet<Statement>();\r
- final Set<Statement> unreliableRight = new HashSet<Statement>();\r
- \r
- while (true) {\r
- if (objectsLeft.isEmpty())\r
- break;\r
- session.syncRequest(new ReadRequest() {\r
- \r
- @Override\r
- public void run(ReadGraph graph) throws DatabaseException {\r
- g = graph;\r
- b = Layer0.getInstance(graph);\r
- // process compares objects that are identified and searches for more resources to process. \r
- process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);\r
- // process unreliable handles cases where unidentified statements subject and object have been identified \r
- processUnreliable(unreliableLeft, unreliableRight);\r
- // process unreliable handles cases where unidentified resources have path of length one to identified resource\r
- processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);\r
- if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {\r
- // comparison is ending, but we have still unprocessed unidentified resources left.\r
- // These cases have longer path than one to identified objects.\r
- processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);\r
- }\r
- }\r
- });\r
- \r
- \r
- \r
- }\r
- for (Statement s : unreliableLeft) {\r
- if (!comparableStatements.containsLeft(s))\r
- addDeletion(s);\r
- }\r
- for (Statement s : unreliableRight) {\r
- if (!comparableStatements.containsRight(s))\r
- addAddition(s);\r
- }\r
- \r
- \r
- }\r
- \r
- private void process(Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {\r
- List<Statement> ss1 = new ArrayList<Statement>();\r
- List<Statement> ss2 = new ArrayList<Statement>();\r
- \r
- while (!objectsLeft.isEmpty()) {\r
- Resource r1 = objectsLeft.pop();\r
- Resource r2 = objectsRight.pop();\r
- \r
- if (r1.equals(r2))\r
- continue;\r
- \r
- if (processedResources.contains(r1))\r
- continue;\r
- processedResources.add(r1);\r
- \r
- \r
- if((comparableResources.containsLeft(r1)||comparableResources.containsRight(r2)) && !comparableResources.contains(r1, r2)) {\r
- throw new DatabaseException("Comparator error: Trying to map " + r1 + " to " + r2 + " while mappings " + r1 + " to " + comparableResources.getRight(r1) + " and " + comparableResources.getLeft(r2) + " to " + r2 + " exist.");\r
- }\r
- addComparable(r1, r2, false);\r
- \r
- //System.out.println("test " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));\r
- compareProps(r1, r2);\r
- \r
- for (Resource rel : tested) {\r
- ss1.addAll(g.getStatements(r1, rel));\r
- ss2.addAll(g.getStatements(r2, rel));\r
- ss1 = filterAsserted(r1, ss1);\r
- ss2 = filterAsserted(r2, ss2);\r
- ss1 = filterTraversed(ss1);\r
- ss2 = filterTraversed(ss2);\r
- ss1 = filterNonTested(ss1);\r
- ss2 = filterNonTested(ss2);\r
- \r
- \r
- compareStatements(ss1, ss2, null, null,null,null);\r
- ss1.clear();\r
- ss2.clear();\r
- }\r
- \r
- for (Resource rel : traversed) {\r
- ss1.addAll(g.getStatements(r1, rel));\r
- ss2.addAll(g.getStatements(r2, rel));\r
- ss1 = filterAsserted(r1, ss1);\r
- ss2 = filterAsserted(r2, ss2);\r
- ss1 = filterNonTraversed(ss1);\r
- ss2 = filterNonTraversed(ss2);\r
- compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);\r
- ss1.clear();\r
- ss2.clear();\r
- \r
- }\r
- }\r
- }\r
- \r
- private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {\r
- MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();\r
- \r
- for (Statement s : unreliableLeft) {\r
- subjectLeft.add(s.getSubject(),s);\r
- objectLeft.add(s.getObject(),s);\r
- }\r
- for (Statement s : unreliableRight) {\r
- subjectRight.add(s.getSubject(),s);\r
- objectRight.add(s.getObject(),s);\r
- }\r
- \r
- for (Resource left : subjectLeft.getKeys()) {\r
- if (!comparableResources.containsLeft(left))\r
- continue;\r
- Resource right = comparableResources.getRight(left);\r
- for (Statement leftS : subjectLeft.getValues(left)) {\r
- Resource leftO = leftS.getObject();\r
- if (!comparableResources.containsLeft(leftO)) \r
- continue;\r
- if (!unreliableLeft.contains(leftS))\r
- continue;\r
- Resource rightO = comparableResources.getRight(leftO);\r
- for (Statement rightS : subjectRight.getValues(right)) {\r
- if (!rightS.getObject().equals(rightO))\r
- continue;\r
- if (!unreliableRight.contains(rightS))\r
- continue;\r
- if (leftS.getPredicate().equals(rightS.getPredicate()) ||\r
- comparableResources.contains(leftS.getPredicate(), rightS.getPredicate())) {\r
- unreliableLeft.remove(leftS);\r
- unreliableRight.remove(rightS);\r
- addComparable(leftS, rightS, false);\r
- }\r
- }\r
- } \r
- }\r
- }\r
- \r
- private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {\r
- MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();\r
- \r
- for (Statement s : unreliableLeft) {\r
- subjectLeft.add(s.getSubject(),s);\r
- objectLeft.add(s.getObject(),s);\r
- }\r
- for (Statement s : unreliableRight) {\r
- subjectRight.add(s.getSubject(),s);\r
- objectRight.add(s.getObject(),s);\r
- }\r
- \r
- for (Resource ol : objectLeft.getKeys()) {\r
- // all statements to the left side object\r
- List<Statement> left = objectLeft.getValues(ol);\r
- // all subjects that have statements to the left side object (ol)\r
- Set<Resource> sLeft = new HashSet<Resource>();\r
- // all matching subjects on the right side\r
- Set<Resource> sRight = new HashSet<Resource>();\r
- for (Statement s : left) {\r
- sLeft.add(s.getSubject());\r
- sRight.add(comparableResources.getRight(s.getSubject()));\r
- }\r
- \r
- // check if object left can be reliably identified by available statements\r
- // if there are any objects on the left side with similar statements, object left cannot be mapped.\r
- boolean hasSimilar = false;\r
- MapList<Resource, Statement> comparableOLeft = new MapList<Resource, Statement>();\r
- for (Resource sl : sLeft) {\r
- for (Statement s : subjectLeft.getValues(sl)) {\r
- if (!s.getObject().equals(ol)) {\r
- comparableOLeft.add(s.getObject(),s);\r
- }\r
- }\r
- }\r
- \r
- for (Resource similarOl : comparableOLeft.getKeys()) {\r
- List<Statement> similarLeft = comparableOLeft.getValues(similarOl);\r
- if (similarLeft.size() == left.size()) {\r
- boolean useL[] = new boolean[left.size()];\r
- boolean useSL[] = new boolean[left.size()];\r
- for (int i = 0; i < left.size(); i++) {\r
- useL[i] = false;\r
- useSL[i] = false;\r
- }\r
- for (int i = 0; i < left.size(); i++) {\r
- for (int j = 0; j < left.size(); j++) {\r
- if (useSL[j])\r
- continue;\r
- Resource pl = left.get(i).getPredicate();\r
- Resource psl = similarLeft.get(j).getPredicate();\r
- if (pl.equals(psl)) {\r
- useL[i] = true;\r
- useSL[j] = true;\r
- break;\r
- }\r
- }\r
- }\r
- boolean diff = false;\r
- for (int i = 0; i < left.size(); i++) {\r
- if (!useL[i] || !useSL[i]) {\r
- diff = true;\r
- }\r
- }\r
- if (!diff) {\r
- hasSimilar = true;\r
- break;\r
- }\r
- }\r
- }\r
- \r
- if (hasSimilar)\r
- continue;\r
- \r
- \r
- // all objects that subjects on the right side point to. Object left has its matching resource among these, if it has matching resource\r
- MapList<Resource,Statement> possibleOR = new MapList<Resource, Statement>();\r
- for (Resource sr : sRight) {\r
- for (Statement s : subjectRight.getValues(sr))\r
- possibleOR.add(s.getObject(),s);\r
- }\r
- \r
- // filter possible right side objects to those that have same amount of statements as the left side object\r
- for (Resource or : possibleOR.getKeys().toArray(new Resource[possibleOR.getKeys().size()])) {\r
- List<Statement> right = possibleOR.getValues(or);\r
- if (right.size() != left.size())\r
- possibleOR.remove(or);\r
- \r
- }\r
- \r
- // check for matching statements (comparable subjects, matching predicates)\r
- MapList<Resource,Statement> matchingOR = new MapList<Resource, Statement>(); // list of objects that have matching statements\r
- Map<Resource,Pair<int[], int[]>> matchingStatements = new HashMap<Resource, Pair<int[], int[]>>(); // matching statements\r
- for (Resource or : possibleOR.getKeys()) {\r
- List<Statement> right = possibleOR.getValues(or);\r
- int iLeft[] = new int[left.size()];\r
- int iRight[] = new int[right.size()];\r
- \r
- for (int i = 0; i < left.size(); i++) {\r
- iLeft[i] = -1;\r
- iRight[i] = -1;\r
- }\r
- \r
- for (int l = 0; l < left.size(); l++) {\r
- Statement ls = left.get(l);\r
- for (int r = 0; r < right.size(); r++) {\r
- if (iRight[r] >= 0)\r
- continue;\r
- Statement rs = right.get(r);\r
- if (!comparableResources.contains(ls.getSubject(), rs.getSubject()))\r
- continue;\r
- if (rcomp.compare(ls.getPredicate(),rs.getPredicate()) == 0) {\r
- iLeft[l] = r;\r
- iRight[r] = l;\r
- break;\r
- }\r
- }\r
- \r
- }\r
- boolean success = true;\r
- for (int i = 0; i < left.size(); i++) {\r
- if (iLeft[i] < 0) {\r
- success = false;\r
- break;\r
- }\r
- if (iRight[i] < 0) {\r
- success = false;\r
- break;\r
- }\r
- \r
- }\r
- if (success) {\r
- for (Statement s : right) \r
- matchingOR.add(or,s);\r
- matchingStatements.put(or, new Pair<int[], int[]>(iLeft, iRight));\r
- }\r
- }\r
- // if there is only one matching right side object, we have found a match \r
- if (matchingOR.getKeySize() == 1) {\r
- Resource or = matchingOR.getKeys().iterator().next();\r
- List<Statement> right = matchingOR.getValues(or);\r
- Pair<int[], int[]> indices = matchingStatements.get(or);\r
- \r
- objectsLeft.add(ol);\r
- objectsRight.add(or);\r
- addComparable(ol, or, false);\r
- for (int l = 0; l < left.size(); l++) {\r
- int r = indices.first[l];\r
- Statement sl = left.get(l);\r
- Statement sr = right.get(r);\r
- addComparable(sl, sr, true);\r
- unreliableLeft.remove(sl);\r
- unreliableRight.remove(sr);\r
- }\r
- \r
- }\r
-\r
- }\r
- \r
- \r
- }\r
- \r
- private void processUnreliableDeep(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {\r
- MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();\r
- MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();\r
- \r
- for (Statement s : unreliableLeft) {\r
- subjectLeft.add(s.getSubject(),s);\r
- objectLeft.add(s.getObject(),s);\r
- }\r
- for (Statement s : unreliableRight) {\r
- subjectRight.add(s.getSubject(),s);\r
- objectRight.add(s.getObject(),s);\r
- }\r
- for (Resource ol : objectLeft.getKeys()) {\r
- Set<Path> pathsLeft = new HashSet<Path>();\r
- for (Resource rel : traversed) {\r
- pathsLeft.addAll(Path.create(g.getStatements(ol, rel)));\r
- }\r
- while (true) {\r
- expand(pathsLeft);\r
- if (pathsLeft.size() == 0)\r
- break;\r
- Collection<Path> endPaths = new ArrayList<Path>(1);\r
- for (Path p : pathsLeft) {\r
- if (comparableResources.containsLeft(p.getEnd())) {\r
- endPaths.add(p);\r
- }\r
- }\r
- if (endPaths.size() > 0) {\r
- pathsLeft.clear();\r
- pathsLeft.addAll(endPaths);\r
- break;\r
- } \r
- }\r
- if (pathsLeft.size() > 0) {\r
- Resource sl = objectLeft.getValues(ol).get(0).getSubject();\r
- Resource sr = comparableResources.getRight(sl);\r
- Collection<Resource> possibleOR = new ArrayList<Resource>();\r
- for (Statement s : subjectRight.getValues(sr)) {\r
- possibleOR.add(s.getObject());\r
- }\r
- Map<Resource,Set<Path>> matchingPaths = new HashMap<Resource, Set<Path>>();\r
- for (Resource or : possibleOR) {\r
- Set<Path> possiblePathsRight = new HashSet<Path>();\r
- for (Path leftPath : pathsLeft) {\r
- possiblePathsRight.addAll(findComparableRight(leftPath, or));\r
- }\r
- if (hasMatchingPaths(pathsLeft, possiblePathsRight)) {\r
- matchingPaths.put(or, possiblePathsRight);\r
- }\r
- }\r
- if (matchingPaths.size() > 0) {\r
- if (matchingPaths.size() == 1) {\r
- Resource or = matchingPaths.keySet().iterator().next();\r
- \r
- objectsLeft.add(ol);\r
- objectsRight.add(or);\r
- addComparable(ol, or, false);\r
- Collection<Statement> statementsLeft = objectLeft.getValues(ol);\r
- Collection<Statement> statementsRight = objectRight.getValues(or);\r
- unreliableLeft.removeAll(statementsLeft);\r
- unreliableRight.removeAll(statementsRight);\r
- BijectionMap<Path,Path> map = getMatchingPaths(pathsLeft, matchingPaths.get(or));\r
- for (Path left : map.getLeftSet()) {\r
- Path right = map.getRight(left);\r
- for (int i = 0; i < left.getLength(); i++) {\r
- addComparable(left.getStatements().get(i),right.getStatements().get(i),false);\r
- }\r
- }\r
- } \r
- }\r
- }\r
- \r
- }\r
- \r
- }\r
- \r
- private boolean hasMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) {\r
- if (leftPaths.size() != rightPaths.size())\r
- return false;\r
- BijectionMap<Path,Path> map = getMatchingPaths(leftPaths, rightPaths);\r
- return map.size() == leftPaths.size();\r
- }\r
- \r
- private BijectionMap<Path,Path> getMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) {\r
- BijectionMap<Path,Path> map = new BijectionMap<Path, Path>();\r
- for (Path leftPath : leftPaths) {\r
- for (Path rightPath : rightPaths) {\r
- if (map.containsRight(rightPath))\r
- continue;\r
- if (leftPath.getLength() != rightPath.getLength())\r
- continue;\r
- if (comparableResources.contains(leftPath.getEnd(), rightPath.getEnd())) {\r
- map.map(leftPath, rightPath);\r
- break;\r
- }\r
- }\r
- }\r
- return map;\r
- }\r
- \r
- private void expand(Set<Path> paths) throws DatabaseException {\r
- Set<Path> stepPathsLeft = new HashSet<Path>();\r
- if (paths.size() == 0)\r
- return;\r
- int length = paths.iterator().next().getLength() + 1;\r
- for (Path p : paths) {\r
- for (Resource rel : traversed) {\r
- stepPathsLeft.addAll(Path.expand(p,g.getStatements(p.getEnd(), rel)));\r
- }\r
- }\r
- paths.clear();\r
- for (Path p : stepPathsLeft) {\r
- if (p.getLength() == length)\r
- paths.add(p);\r
- }\r
- }\r
- \r
- private Collection<Path> findComparableRight(Path leftPath, Resource beginRight) throws DatabaseException {\r
- Set<Path> rightPaths = new HashSet<Path>();\r
- rightPaths.addAll(Path.create(g.getStatements(beginRight, getRight(leftPath.getStatements().get(0).getPredicate()))));\r
- for (int i = 1; i < leftPath.getLength(); i++) {\r
- if (rightPaths.size() == 0)\r
- return rightPaths;\r
- Set<Path> stepPaths = new HashSet<Path>();\r
- for (Path p : rightPaths) {\r
- stepPaths.addAll(Path.expand(p, g.getStatements(p.getEnd(), getRight(leftPath.getStatements().get(i).getPredicate()))));\r
- }\r
- rightPaths.clear();\r
- for (Path p : stepPaths)\r
- if (p.getLength() == i+1) \r
- rightPaths.add(p);\r
- }\r
- return rightPaths;\r
- \r
- }\r
- \r
- private Resource getRight(Resource r) {\r
- if (comparableResources.containsLeft(r))\r
- return comparableResources.getRight(r);\r
- return r;\r
- }\r
- \r
-\r
- \r
- public BijectionMap<Statement, Statement> getComparableStatements() {\r
- return comparableStatements;\r
- }\r
- \r
- public BijectionMap<Resource, Resource> getComparableResources() {\r
- return comparableResources;\r
- }\r
- \r
- public GraphChanges getChanges() {\r
- return new GraphChanges(r1,r2,changes1,changes2,modifications,comparableResources);\r
- }\r
- \r
- private void addComparable(Statement left, Statement right, boolean process) throws DatabaseException {\r
- addComparable(left.getObject(), right.getObject(), process);\r
- comparableStatements.map(left, right);\r
- //comparableResources.map(left.getObject(), right.getObject());\r
- }\r
- \r
- private void addComparable(Resource left, Resource right, boolean process) throws DatabaseException {\r
- if(!comparableResources.contains(left, right)) {\r
- if (comparableResources.containsLeft(left)||comparableResources.containsRight(right)) {\r
- throw new DatabaseException("Comparator error: Trying to map " + left + " to " + right + " while mappings " + left + " to " + comparableResources.getRight(left) + " and " + comparableResources.getLeft(right) + " to " + right + " exist.");\r
- } else {\r
- if (DEBUG) System.out.println(left + " = " + right);\r
- comparableResources.map(left, right); \r
- }\r
- }\r
- \r
- }\r
- \r
- public List<Statement> filterAsserted(Resource r, Collection<Statement> in) throws DatabaseException {\r
- List<Statement> out = new ArrayList<Statement>();\r
- for (Statement s : in) {\r
- if (!s.isAsserted(r))\r
- out.add(s);\r
- \r
- }\r
- return out;\r
- }\r
- \r
- \r
-\r
- private String printStatement(ReadGraph graph, Statement s) throws DatabaseException {\r
- return NameUtils.getSafeName(graph, s.getSubject()) + " " + NameUtils.getSafeName(graph, s.getPredicate()) + " " + NameUtils.getSafeName(graph, s.getObject());\r
- }\r
- \r
- private List<Statement> filterTraversed(List<Statement> in) throws DatabaseException {\r
- return filter(traversed, in);\r
- }\r
- \r
- private List<Statement> filterNonTested(List<Statement> in) throws DatabaseException {\r
- return filter(nonTested, in);\r
- }\r
- \r
- private List<Statement> filterNonTraversed(List<Statement> in) throws DatabaseException {\r
- return filter(nonTraversed, in);\r
- }\r
- \r
- private List<Statement> filter(Collection<Resource> toFilter, List<Statement> in) throws DatabaseException {\r
- if (toFilter.size() == 0)\r
- return in;\r
- List<Statement> out = new ArrayList<Statement>();\r
- for (Statement s : in) {\r
- boolean usable = true;\r
- for (Resource r : toFilter) {\r
- if (g.isSubrelationOf(s.getPredicate(),r)) {\r
- usable = false;\r
- break;\r
- }\r
- }\r
- if (usable) {\r
- out.add(s);\r
- }\r
- \r
- }\r
- return out;\r
- }\r
- \r
- \r
- private void addDeletion(Statement s) {\r
- if (!changes1Set.contains(s)) {\r
- changes1Set.add(s);\r
- changes1.add(s);\r
- }\r
- }\r
- \r
- private void addAddition(Statement s) {\r
- if (!changes2Set.contains(s)) {\r
- changes2Set.add(s);\r
- changes2.add(s);\r
- }\r
- }\r
- \r
- private void addModification(Statement s1, Statement s2) {\r
- Pair<Statement, Statement> mod = new Pair<Statement, Statement>(s1,s2);\r
- if (!modificationsSet.contains(mod)) {\r
- modificationsSet.add(mod);\r
- modifications.add(mod);\r
- }\r
- }\r
- \r
- public void sortStatement(List<Statement> list1, List<Statement> list2) {\r
- sortStatement(list1, list2, scomp);\r
- }\r
- \r
- public void sortStatement(List<Statement> list1, List<Statement> list2, Comparator<Statement> scomp) {\r
- Collections.sort(list1,scomp);\r
- Collections.sort(list2,scomp);\r
- \r
- List<Statement> sorted1 = new ArrayList<Statement>(list1.size());\r
- List<Statement> sorted2 = new ArrayList<Statement>(list2.size());\r
- sorted1.addAll(list1);\r
- sorted2.addAll(list2);\r
- \r
- int ss1 = 0;\r
- int ss2 = 0;\r
- for (int i = 0; i < list1.size(); ) {\r
- Statement s1 = list1.get(i);\r
- int same1 = sameRel(list1, i); \r
- for (int j = 0; j < list2.size(); j++) {\r
- Statement s2 = list2.get(j);\r
- if (scomp.compare(s1, s2) == 0) {\r
- int same2 = sameRel(list2, j);\r
- copy(sorted1,ss1,list1,i,same1);\r
- ss1 += same1;\r
- copy(sorted2,ss2,list2,j,same2);\r
- ss2 += same2;\r
- break;\r
- }\r
- }\r
- i+= same1;\r
- }\r
- if (ss1 < sorted1.size()) {\r
- for (Statement s : list1) {\r
- if (!sorted1.contains(s)) {\r
- sorted1.set(ss1,s);\r
- ss1++;\r
- }\r
- }\r
- }\r
- if (ss2 < sorted2.size()) {\r
- for (Statement s : list2) {\r
- if (!sorted2.contains(s)) {\r
- sorted2.set(ss2,s);\r
- ss2++;\r
- }\r
- }\r
- }\r
- \r
- list1.clear();\r
- list2.clear();\r
- list1.addAll(sorted1);\r
- list2.addAll(sorted2);\r
- }\r
- \r
- public <T> void copy(List<T> to, int toIndex, List<T> from, int fromIndex, int amount) {\r
- for (int i = 0; i < amount; i++) {\r
- to.set(toIndex + i, from.get(fromIndex+ i));\r
- }\r
- }\r
- \r
- public void sortResource(List<Resource> list1, List<Resource> list2) {\r
- Collections.sort(list1,rcomp);\r
- int js = 0;\r
- for (int i = 0; i < list1.size(); i++) {\r
- Resource s1 = list1.get(i);\r
- for (int j = js; j < list2.size(); j++) {\r
- Resource s2 = list2.get(j);\r
- if (rcomp.compare(s1, s2) == 0) {\r
- Resource t = list2.get(js);\r
- list2.set(js, s2);\r
- list2.set(j, t);\r
- break;\r
- }\r
- }\r
- js++;\r
- }\r
- }\r
- \r
- private void compareStatements(List<Statement> ss1, List<Statement> ss2, Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {\r
- sortStatement(ss1, ss2);\r
- \r
- int i1 = 0;\r
- int i2 = 0;\r
- \r
- while (true) {\r
- if (i1 >= ss1.size()) {\r
- if (i2 >= ss2.size()) {\r
- break;\r
- } else {\r
- while (i2 < ss2.size()) {\r
- if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i2)));\r
- \r
- addAddition(ss2.get(i2));\r
- i2++;\r
- }\r
- break;\r
- }\r
- } else if (i2 >= ss2.size()) {\r
- while (i1 < ss1.size()) {\r
- if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i1)));\r
- addDeletion(ss1.get(i1));\r
- i1++;\r
- }\r
- break;\r
- }\r
- int same1 = sameRel(ss1, i1);\r
- int same2 = sameRel(ss2, i2);\r
- int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());\r
- if (c == 0) {\r
- compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight);\r
- i1+=same1;\r
- i2+=same2;\r
- } else if (c < 0) {\r
- for (int i = 0; i < same1; i++) {\r
- if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i+i1)));\r
- addDeletion(ss1.get(i+i1));\r
- }\r
- i1 += same1;\r
- } else {\r
- for (int i = 0; i < same2; i++) {\r
- if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i+i2)));\r
- addAddition(ss2.get(i+i2));\r
- }\r
- \r
- i2 += same2;\r
- }\r
- }\r
- }\r
- \r
-\r
- \r
- private int sameRel(List<Statement> statements, int off) {\r
- if (statements.size() <= off)\r
- return 0;\r
- int same = 1;\r
- long id = statements.get(off).getPredicate().getResourceId();\r
- for (int i = off+1; i <statements.size(); i++) {\r
- if (statements.get(i).getPredicate().getResourceId() == id)\r
- same++;\r
- else \r
- break;\r
- }\r
- return same;\r
- \r
- }\r
-\r
- private int compareObject(Resource o1, Resource o2) throws DatabaseException {\r
- if (o1.equals(o2))\r
- return -1;\r
- if (comparableResources.contains(o1, o2))\r
- return (-1);\r
- if (comparableResources.containsLeft(o1))\r
- return Integer.MAX_VALUE;\r
- if (comparableResources.containsRight(o2))\r
- return Integer.MAX_VALUE;\r
- return comparator.compare(g, o1, o2);\r
- }\r
- \r
- private void compareStatements(List<Statement> ss1, int off1, int len1, List<Statement> ss2, int off2, int len2, Collection<Resource> objectsLeft, Collection<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {\r
- boolean[] used1 = new boolean[len1];\r
- for (int i = 0; i < used1.length; i++) {\r
- used1[i] = false;\r
- }\r
- \r
- boolean[] used2 = new boolean[len2];\r
- for (int i = 0; i < used2.length; i++) {\r
- used2[i] = false;\r
- }\r
- \r
- // left, right, difference\r
- List<List<Integer>> differences = new ArrayList<List<Integer>>();\r
- for (int i1 = off1; i1 < off1 + len1; i1++) {\r
- Statement s1 = ss1.get(i1);\r
- List<Integer> diff = new ArrayList<Integer>();\r
- for (int i2 = off2; i2 < off2 + len2; i2++) {\r
- Statement s2 = ss2.get(i2);\r
- int d = compareObject(s1.getObject(), s2.getObject());\r
- if (d == 0) {\r
- for (Resource t : strong) {\r
- if (s1.getPredicate().equals(t) || g.isSubrelationOf(s1.getPredicate(), t)) {\r
- d = 1;\r
- break;\r
- }\r
- }\r
- }\r
- diff.add(d);\r
- }\r
- differences.add(diff);\r
- }\r
- // difference, left\r
- MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();\r
- for (int i = 0; i < differences.size(); i++) {\r
- List<Integer> list = differences.get(i);\r
- for (int j = 0; j < list.size(); j++) {\r
- priorities.add(list.get(j), i);\r
- }\r
- }\r
- \r
- Integer[] pris = priorities.getKeys(new Integer[]{});\r
- Arrays.sort(pris);\r
- \r
- for (Integer pri : pris) {\r
- if (pri == Integer.MAX_VALUE) {\r
-\r
- } else if (pri == 0) {\r
- \r
- } else {\r
- List<Integer> i1s = priorities.getValues(pri);\r
- for (Integer i1 : i1s) {\r
- if (used1[i1])\r
- continue;\r
- List<Integer> i2diff = differences.get(i1);\r
- for (int i2 = 0; i2 < i2diff.size(); i2++) {\r
- if (i2diff.get(i2) == pri) {\r
- if (used2[i2])\r
- continue;\r
- used1[i1] = true;\r
- used2[i2] = true;\r
- Statement s1 = ss1.get(i1+off1);\r
- Statement s2 = ss2.get(i2+off2);\r
- \r
- if (objectsLeft != null) {\r
- objectsLeft.add(s1.getObject());\r
- objectsRight.add(s2.getObject());\r
- } \r
- addComparable(s1, s2, true);\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- for (Integer pri : pris) {\r
- if (pri != 0)\r
- continue;\r
- Set<Statement> s1s = new HashSet<Statement>();\r
- Set<Statement> s2s = new HashSet<Statement>();\r
- Set<Integer> s1i = new HashSet<Integer>();\r
- Set<Integer> s2i = new HashSet<Integer>();\r
- List<Integer> i1s = priorities.getValues(pri);\r
- for (Integer i1 : i1s) {\r
- if (used1[i1])\r
- continue;\r
- List<Integer> i2diff = differences.get(i1);\r
- for (int i2 = 0; i2 < i2diff.size(); i2++) {\r
- if (i2diff.get(i2) == pri) {\r
- if (used2[i2])\r
- continue;\r
- Statement s1 = ss1.get(i1+off1);\r
- Statement s2 = ss2.get(i2+off2);\r
- s1s.add(s1);\r
- s2s.add(s2);\r
- s1i.add(i1);\r
- s2i.add(i2);\r
- }\r
- }\r
- }\r
- if (unreliableLeft != null) {\r
- unreliableLeft.addAll(s1s);\r
- unreliableRight.addAll(s2s);\r
- }\r
- for (Integer i : s1i)\r
- used1[i] = true;\r
- for (Integer i : s2i)\r
- used2[i] = true;\r
-\r
- }\r
- for (int i1 = off1; i1 < off1 + len1; i1++) {\r
- if (!used1[i1-off1]) {\r
- if (DEBUG) System.out.println("Compare Object deletion " + printStatement(g,ss1.get(i1)));\r
- addDeletion(ss1.get(i1));\r
- }\r
- }\r
- for (int i2 = off2; i2 < off2 + len2; i2++) {\r
- if (!used2[i2-off2]) {\r
- if (DEBUG) System.out.println("Compare Object addition " + printStatement(g,ss2.get(i2)));\r
- addAddition(ss2.get(i2));\r
- }\r
- }\r
- }\r
- \r
- \r
- \r
- /**\r
- * compares properties, assumes functional relations\r
- * @param r1\r
- * @param r2\r
- * @throws ServiceException\r
- * @throws DoesNotContainValueException\r
- * @throws ValidationException \r
- */\r
- private void compareProps(Resource r1, Resource r2) throws DatabaseException {\r
- if (DEBUG) System.out.println("compareProps " + r1 + " " + NameUtils.getSafeName(g, r1) + " " + r2 + " " + NameUtils.getSafeName(g, r2));\r
- List<Statement> ss1 = new ArrayList<Statement>();\r
- List<Statement> ss2 = new ArrayList<Statement>();\r
- ss1.addAll(g.getStatements(r1, b.HasProperty));\r
- ss2.addAll(g.getStatements(r2, b.HasProperty));\r
- ss1 = filterNonTested(ss1);\r
- ss2 = filterNonTested(ss2);\r
- sortStatement(ss1, ss2);\r
- \r
- int i1 = 0; \r
- int i2 = 0;\r
- \r
- while (true) {\r
- if (i1 >= ss1.size()) {\r
- if (i2 >= ss2.size())\r
- break;\r
- else {\r
- while (i2 < ss2.size()) {\r
- if (DEBUG) System.out.println("Compare Prop diff2 " + printStatement(g,ss2.get(i2)));\r
- addAddition(ss2.get(i2));\r
- i2++;\r
- }\r
- break;\r
- }\r
- } else if (i2 >= ss2.size()) {\r
- while (i1 < ss1.size()) {\r
- if (DEBUG) System.out.println("Compare Prop diff1 " + printStatement(g,ss1.get(i1)));\r
- addDeletion(ss1.get(i1));\r
- i1++;\r
- }\r
- break;\r
- }\r
- Statement s1 = ss1.get(i1);\r
- Statement s2 = ss2.get(i2);\r
- if (s1.isAsserted(r1) && s2.isAsserted(r2)) {\r
- i1++;\r
- i2++;\r
- continue;\r
- }\r
- int c = scomp.compare(s1, s2);\r
- switch (c) {\r
- case 0:{\r
- boolean b1 = g.hasValue(s1.getObject());\r
- boolean b2 = g.hasValue(s2.getObject());\r
- if (b1 == b2) {\r
- if (b1) {\r
- Object v1 = g.getValue(s1.getObject());\r
- Object v2 = g.getValue(s2.getObject());\r
- boolean eq = compareValue(v1, v2);\r
- if (!eq) {\r
- addModification(s1, s2);\r
- addComparable(s1, s2, false);\r
- }\r
- } else {\r
- if (!s1.getObject().equals(s1.getSubject()) && !s2.getObject().equals(s2.getSubject()))\r
- compareProps(s1.getObject(), s2.getObject());\r
- }\r
- } else {\r
- addModification(s1, s2);\r
- addComparable(s1, s2, false);\r
- }\r
- i1++;\r
- i2++;\r
- break;\r
- }\r
- case -1:{\r
- if (DEBUG) System.out.println("Compare Prop diff1s " + printStatement(g,s1));\r
- addDeletion(s1);\r
- i1++;\r
- break;\r
- }\r
- \r
- case 1:{\r
- if (DEBUG) System.out.println("Compare Prop diff2s " + printStatement(g,s2));\r
- addAddition(s2);\r
- i2++;\r
- break;\r
- }\r
- }\r
-\r
- }\r
- \r
- ss1.clear();\r
- ss2.clear();\r
- \r
- }\r
- \r
- public static boolean compareValue(Object v1, Object v2) {\r
- if (v1 instanceof Object[] && v2 instanceof Object[])\r
- return Arrays.deepEquals((Object[])v1, (Object[])v2);\r
- else if (v1 instanceof int[] && v2 instanceof int[]) \r
- return Arrays.equals((int[])v1, (int[])v2);\r
- else if (v1 instanceof float[] && v2 instanceof float[]) \r
- return Arrays.equals((float[])v1, (float[])v2);\r
- else if (v1 instanceof double[] && v2 instanceof double[]) \r
- return Arrays.equals((double[])v1, (double[])v2);\r
- else if (v1 instanceof long[] && v2 instanceof long[]) \r
- return Arrays.equals((long[])v1, (long[])v2);\r
- else if (v1 instanceof byte[] && v2 instanceof byte[]) \r
- return Arrays.equals((byte[])v1, (byte[])v2);\r
- else if (v1 instanceof boolean[] && v2 instanceof boolean[]) \r
- return Arrays.equals((boolean[])v1, (boolean[])v2);\r
- else\r
- return v1.equals(v2);\r
- }\r
-\r
- \r
- public class PredicateComparator implements Comparator<Statement> {\r
- @Override\r
- public int compare(Statement o1, Statement o2) {\r
- if (comparableResources.contains(o1.getPredicate(), o2.getPredicate()))\r
- return 0;\r
- if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())\r
- return -1;\r
- if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())\r
- return 1;\r
- return 0;\r
- }\r
- }\r
- \r
- public class SubjectComparator implements Comparator<Statement> {\r
- @Override\r
- public int compare(Statement o1, Statement o2) {\r
- if (comparableResources.contains(o1.getSubject(), o2.getSubject()))\r
- return 0;\r
- if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())\r
- return -1;\r
- if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())\r
- return 1;\r
- return 0;\r
- }\r
- }\r
- \r
- public class ObjectComparator implements Comparator<Statement> {\r
- @Override\r
- public int compare(Statement o1, Statement o2) {\r
- if (comparableResources.contains(o1.getObject(), o2.getObject()))\r
- return 0;\r
- if (o1.getObject().getResourceId() < o2.getObject().getResourceId())\r
- return -1;\r
- if (o1.getObject().getResourceId() > o2.getObject().getResourceId())\r
- return 1;\r
- return 0;\r
- }\r
- }\r
- \r
- public static class FullStatementComparator implements Comparator<Statement> {\r
- @Override\r
- public int compare(Statement o1, Statement o2) {\r
- if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())\r
- return -1;\r
- if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())\r
- return 1;\r
- if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())\r
- return -1;\r
- if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())\r
- return 1;\r
- if (o1.getObject().getResourceId() < o2.getObject().getResourceId())\r
- return -1;\r
- if (o1.getObject().getResourceId() > o2.getObject().getResourceId())\r
- return 1;\r
- return 0;\r
- }\r
- }\r
- \r
- public class ResComparator implements Comparator<Resource> {\r
- @Override\r
- public int compare(Resource o1, Resource o2) {\r
- if (comparableResources.contains(o1, o2))\r
- return 0;\r
- if (o1.getResourceId() < o2.getResourceId())\r
- return -1;\r
- if (o1.getResourceId() > o2.getResourceId())\r
- return 1;\r
- return 0;\r
- }\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Foster Wheeler Energia Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.interop.test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.Statement;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.datastructures.BijectionMap;
+import org.simantics.utils.datastructures.MapList;
+import org.simantics.utils.datastructures.Pair;
+
+/**
+ * Compares two subgraphs and reports differences.
+ *
+ * Assumes that subgraphs (defined using traverse relations) are not cyclic.
+ *
+ * Assumes that properties can be used to identify objects, if relation type is not enough.
+ *
+ *
+ *
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ *
+ */
+public class GraphComparator {
+
+ private static final boolean DEBUG = false;
+
+ private Resource r1;
+ private Resource r2;
+ private Set<Resource> strong = new HashSet<Resource>(); // List of relations that identify object, if subject is already identified.
+ private List<Resource> traversed = new ArrayList<Resource>(); // list of relations that are traversed (and tested)
+ private List<Resource> tested = new ArrayList<Resource>(); // list of relations that are tested, but not traversed
+ private List<Resource> nonTraversed = new ArrayList<Resource>(); // list of relations that are not traversed
+ private List<Resource> nonTested = new ArrayList<Resource>(); // list of relations that are not tested
+
+ private List<Statement> changes1 = new ArrayList<Statement>();
+ private List<Statement> changes2 = new ArrayList<Statement>();
+ private List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
+ private Set<Statement> changes1Set = new HashSet<Statement>();
+ private Set<Statement> changes2Set = new HashSet<Statement>();
+ private Set<Pair<Statement,Statement>> modificationsSet = new HashSet<Pair<Statement,Statement>>();
+
+ private BijectionMap<Statement, Statement> comparableStatements = new BijectionMap<Statement, Statement>();
+ private BijectionMap<Resource, Resource> comparableResources = new BijectionMap<Resource, Resource>();
+
+ private Set<Resource> processedResources = new HashSet<Resource>();
+
+ private ResourceComparator comparator;
+
+ private Comparator<Statement> scomp = new PredicateComparator();
+ private Comparator<Resource> rcomp = new ResComparator();
+
+ private Set<Resource> nonMatchedLeft = new HashSet<Resource>();
+ private Set<Resource> nonMatchedRight = new HashSet<Resource>();
+
+ // runtime attributes
+
+ private ReadGraph g;
+ private Layer0 b;
+
+ public GraphComparator(Resource r1, Resource r2) {
+ this.r1 = r1;
+ this.r2 = r2;
+ comparator = new TypeComparator();
+ }
+
+ public GraphComparator(Resource r1, Resource r2, ResourceComparator comparator) {
+ this.r1 = r1;
+ this.r2 = r2;
+ this.comparator = comparator;
+ }
+
+ ArrayList<Statement> ss1 = new ArrayList<Statement>();
+ ArrayList<Statement> ss2 = new ArrayList<Statement>();
+
+
+ public Comparator<Resource> getResourceComparator() {
+ return rcomp;
+ }
+
+ public Comparator<Statement> getStatementComparator() {
+ return scomp;
+ }
+
+ public Resource getR1() {
+ return r1;
+ }
+
+ public Resource getR2() {
+ return r2;
+ }
+
+ public void addTraversed(Resource rel) {
+ traversed.add(rel);
+ }
+
+ public void addTraversed(Collection<Resource> rels) {
+ traversed.addAll(rels);
+ }
+
+ public void addNonTraversed(Resource rel) {
+ nonTraversed.add(rel);
+ }
+
+ public void addNonTraversed(Collection<Resource> rels) {
+ nonTraversed.addAll(rels);
+ }
+
+ public void addTested(Resource rel) {
+ tested.add(rel);
+ }
+
+ public void addTested(Collection<Resource> rels) {
+ tested.addAll(rels);
+ }
+
+ public void addNonTested(Resource rel) {
+ nonTested.add(rel);
+ }
+
+ public void addNonTested(Collection<Resource> rels) {
+ nonTested.addAll(rels);
+ }
+
+ public void addComparableResources(Resource r1, Resource r2) {
+ comparableResources.map(r1, r2);
+ }
+
+ public void addComparableResources(BijectionMap<Resource, Resource> matching) {
+ comparableResources.addAll(matching);
+ }
+
+ public void addStrong(Resource r) {
+ strong.add(r);
+ }
+
+ public void addStrong(Collection<Resource> rels) {
+ strong.addAll(rels);
+ }
+
+ public void addNonMatchedLeft(Resource r) {
+ nonMatchedLeft.add(r);
+ }
+
+ public void addNonMatchedRight(Resource r) {
+ nonMatchedRight.add(r);
+ }
+
+ public void test(ReadGraph g) throws DatabaseException {
+ this.g = g;
+ this.b = Layer0.getInstance(g);
+ comparator.setComparator(this);
+
+ Stack<Resource> objectsLeft = new Stack<Resource>();
+ Stack<Resource> objectsRight = new Stack<Resource>();
+ objectsLeft.push(r1);
+ objectsRight.push(r2);
+
+ Set<Statement> unreliableLeft = new HashSet<Statement>();
+ Set<Statement> unreliableRight = new HashSet<Statement>();
+
+ while (true) {
+ if (objectsLeft.isEmpty())
+ break;
+
+
+ // process compares objects that are identified and searches for more resources to process.
+ process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
+ // process unreliable handles cases where unidentified statements subject and object have been identified
+ processUnreliable(unreliableLeft, unreliableRight);
+ // process unreliable handles cases where unidentified resources have path of length one to identified resource
+ processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
+ if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
+ // comparison is ending, but we have still unprocessed unidentified resources left.
+ // These cases have longer path than one to identified objects.
+ processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
+ }
+
+ }
+ for (Statement s : unreliableLeft) {
+ if (!comparableStatements.containsLeft(s))
+ addDeletion(s);
+ }
+ for (Statement s : unreliableRight) {
+ if (!comparableStatements.containsRight(s))
+ addAddition(s);
+ }
+
+
+ }
+
+ public void test(Session session) throws DatabaseException {
+ test(session, r1, r2);
+ }
+
+ public void test(Session session, Resource r1, Resource r2) throws DatabaseException {
+
+ comparator.setComparator(this);
+
+ addComparable(r1, r2, false);
+
+ final Stack<Resource> objectsLeft = new Stack<Resource>();
+ final Stack<Resource> objectsRight = new Stack<Resource>();
+ objectsLeft.push(r1);
+ objectsRight.push(r2);
+
+ final Set<Statement> unreliableLeft = new HashSet<Statement>();
+ final Set<Statement> unreliableRight = new HashSet<Statement>();
+
+ while (true) {
+ if (objectsLeft.isEmpty())
+ break;
+ session.syncRequest(new ReadRequest() {
+
+ @Override
+ public void run(ReadGraph graph) throws DatabaseException {
+ g = graph;
+ b = Layer0.getInstance(graph);
+ // process compares objects that are identified and searches for more resources to process.
+ process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
+ // process unreliable handles cases where unidentified statements subject and object have been identified
+ processUnreliable(unreliableLeft, unreliableRight);
+ // process unreliable handles cases where unidentified resources have path of length one to identified resource
+ processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
+ if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
+ // comparison is ending, but we have still unprocessed unidentified resources left.
+ // These cases have longer path than one to identified objects.
+ processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
+ }
+ }
+ });
+
+
+
+ }
+ for (Statement s : unreliableLeft) {
+ if (!comparableStatements.containsLeft(s))
+ addDeletion(s);
+ }
+ for (Statement s : unreliableRight) {
+ if (!comparableStatements.containsRight(s))
+ addAddition(s);
+ }
+
+
+ }
+
+ private void process(Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
+ List<Statement> ss1 = new ArrayList<Statement>();
+ List<Statement> ss2 = new ArrayList<Statement>();
+
+ while (!objectsLeft.isEmpty()) {
+ Resource r1 = objectsLeft.pop();
+ Resource r2 = objectsRight.pop();
+
+ if (r1.equals(r2))
+ continue;
+
+ if (processedResources.contains(r1))
+ continue;
+ processedResources.add(r1);
+
+
+ if((comparableResources.containsLeft(r1)||comparableResources.containsRight(r2)) && !comparableResources.contains(r1, r2)) {
+ throw new DatabaseException("Comparator error: Trying to map " + r1 + " to " + r2 + " while mappings " + r1 + " to " + comparableResources.getRight(r1) + " and " + comparableResources.getLeft(r2) + " to " + r2 + " exist.");
+ }
+ addComparable(r1, r2, false);
+
+ //System.out.println("test " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));
+ compareProps(r1, r2);
+
+ for (Resource rel : tested) {
+ ss1.addAll(g.getStatements(r1, rel));
+ ss2.addAll(g.getStatements(r2, rel));
+ ss1 = filterAsserted(r1, ss1);
+ ss2 = filterAsserted(r2, ss2);
+ ss1 = filterTraversed(ss1);
+ ss2 = filterTraversed(ss2);
+ ss1 = filterNonTested(ss1);
+ ss2 = filterNonTested(ss2);
+
+
+ compareStatements(ss1, ss2, null, null,null,null);
+ ss1.clear();
+ ss2.clear();
+ }
+
+ for (Resource rel : traversed) {
+ ss1.addAll(g.getStatements(r1, rel));
+ ss2.addAll(g.getStatements(r2, rel));
+ ss1 = filterAsserted(r1, ss1);
+ ss2 = filterAsserted(r2, ss2);
+ ss1 = filterNonTraversed(ss1);
+ ss2 = filterNonTraversed(ss2);
+ compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
+ ss1.clear();
+ ss2.clear();
+
+ }
+ }
+ }
+
+ private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
+ MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
+
+ for (Statement s : unreliableLeft) {
+ subjectLeft.add(s.getSubject(),s);
+ objectLeft.add(s.getObject(),s);
+ }
+ for (Statement s : unreliableRight) {
+ subjectRight.add(s.getSubject(),s);
+ objectRight.add(s.getObject(),s);
+ }
+
+ for (Resource left : subjectLeft.getKeys()) {
+ if (!comparableResources.containsLeft(left))
+ continue;
+ Resource right = comparableResources.getRight(left);
+ for (Statement leftS : subjectLeft.getValues(left)) {
+ Resource leftO = leftS.getObject();
+ if (!comparableResources.containsLeft(leftO))
+ continue;
+ if (!unreliableLeft.contains(leftS))
+ continue;
+ Resource rightO = comparableResources.getRight(leftO);
+ for (Statement rightS : subjectRight.getValues(right)) {
+ if (!rightS.getObject().equals(rightO))
+ continue;
+ if (!unreliableRight.contains(rightS))
+ continue;
+ if (leftS.getPredicate().equals(rightS.getPredicate()) ||
+ comparableResources.contains(leftS.getPredicate(), rightS.getPredicate())) {
+ unreliableLeft.remove(leftS);
+ unreliableRight.remove(rightS);
+ addComparable(leftS, rightS, false);
+ }
+ }
+ }
+ }
+ }
+
+ private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
+ MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
+
+ for (Statement s : unreliableLeft) {
+ subjectLeft.add(s.getSubject(),s);
+ objectLeft.add(s.getObject(),s);
+ }
+ for (Statement s : unreliableRight) {
+ subjectRight.add(s.getSubject(),s);
+ objectRight.add(s.getObject(),s);
+ }
+
+ for (Resource ol : objectLeft.getKeys()) {
+ // all statements to the left side object
+ List<Statement> left = objectLeft.getValues(ol);
+ // all subjects that have statements to the left side object (ol)
+ Set<Resource> sLeft = new HashSet<Resource>();
+ // all matching subjects on the right side
+ Set<Resource> sRight = new HashSet<Resource>();
+ for (Statement s : left) {
+ sLeft.add(s.getSubject());
+ sRight.add(comparableResources.getRight(s.getSubject()));
+ }
+
+ // check if object left can be reliably identified by available statements
+ // if there are any objects on the left side with similar statements, object left cannot be mapped.
+ boolean hasSimilar = false;
+ MapList<Resource, Statement> comparableOLeft = new MapList<Resource, Statement>();
+ for (Resource sl : sLeft) {
+ for (Statement s : subjectLeft.getValues(sl)) {
+ if (!s.getObject().equals(ol)) {
+ comparableOLeft.add(s.getObject(),s);
+ }
+ }
+ }
+
+ for (Resource similarOl : comparableOLeft.getKeys()) {
+ List<Statement> similarLeft = comparableOLeft.getValues(similarOl);
+ if (similarLeft.size() == left.size()) {
+ boolean useL[] = new boolean[left.size()];
+ boolean useSL[] = new boolean[left.size()];
+ for (int i = 0; i < left.size(); i++) {
+ useL[i] = false;
+ useSL[i] = false;
+ }
+ for (int i = 0; i < left.size(); i++) {
+ for (int j = 0; j < left.size(); j++) {
+ if (useSL[j])
+ continue;
+ Resource pl = left.get(i).getPredicate();
+ Resource psl = similarLeft.get(j).getPredicate();
+ if (pl.equals(psl)) {
+ useL[i] = true;
+ useSL[j] = true;
+ break;
+ }
+ }
+ }
+ boolean diff = false;
+ for (int i = 0; i < left.size(); i++) {
+ if (!useL[i] || !useSL[i]) {
+ diff = true;
+ }
+ }
+ if (!diff) {
+ hasSimilar = true;
+ break;
+ }
+ }
+ }
+
+ if (hasSimilar)
+ continue;
+
+
+ // all objects that subjects on the right side point to. Object left has its matching resource among these, if it has matching resource
+ MapList<Resource,Statement> possibleOR = new MapList<Resource, Statement>();
+ for (Resource sr : sRight) {
+ for (Statement s : subjectRight.getValues(sr))
+ possibleOR.add(s.getObject(),s);
+ }
+
+ // filter possible right side objects to those that have same amount of statements as the left side object
+ for (Resource or : possibleOR.getKeys().toArray(new Resource[possibleOR.getKeys().size()])) {
+ List<Statement> right = possibleOR.getValues(or);
+ if (right.size() != left.size())
+ possibleOR.remove(or);
+
+ }
+
+ // check for matching statements (comparable subjects, matching predicates)
+ MapList<Resource,Statement> matchingOR = new MapList<Resource, Statement>(); // list of objects that have matching statements
+ Map<Resource,Pair<int[], int[]>> matchingStatements = new HashMap<Resource, Pair<int[], int[]>>(); // matching statements
+ for (Resource or : possibleOR.getKeys()) {
+ List<Statement> right = possibleOR.getValues(or);
+ int iLeft[] = new int[left.size()];
+ int iRight[] = new int[right.size()];
+
+ for (int i = 0; i < left.size(); i++) {
+ iLeft[i] = -1;
+ iRight[i] = -1;
+ }
+
+ for (int l = 0; l < left.size(); l++) {
+ Statement ls = left.get(l);
+ for (int r = 0; r < right.size(); r++) {
+ if (iRight[r] >= 0)
+ continue;
+ Statement rs = right.get(r);
+ if (!comparableResources.contains(ls.getSubject(), rs.getSubject()))
+ continue;
+ if (rcomp.compare(ls.getPredicate(),rs.getPredicate()) == 0) {
+ iLeft[l] = r;
+ iRight[r] = l;
+ break;
+ }
+ }
+
+ }
+ boolean success = true;
+ for (int i = 0; i < left.size(); i++) {
+ if (iLeft[i] < 0) {
+ success = false;
+ break;
+ }
+ if (iRight[i] < 0) {
+ success = false;
+ break;
+ }
+
+ }
+ if (success) {
+ for (Statement s : right)
+ matchingOR.add(or,s);
+ matchingStatements.put(or, new Pair<int[], int[]>(iLeft, iRight));
+ }
+ }
+ // if there is only one matching right side object, we have found a match
+ if (matchingOR.getKeySize() == 1) {
+ Resource or = matchingOR.getKeys().iterator().next();
+ List<Statement> right = matchingOR.getValues(or);
+ Pair<int[], int[]> indices = matchingStatements.get(or);
+
+ objectsLeft.add(ol);
+ objectsRight.add(or);
+ addComparable(ol, or, false);
+ for (int l = 0; l < left.size(); l++) {
+ int r = indices.first[l];
+ Statement sl = left.get(l);
+ Statement sr = right.get(r);
+ addComparable(sl, sr, true);
+ unreliableLeft.remove(sl);
+ unreliableRight.remove(sr);
+ }
+
+ }
+
+ }
+
+
+ }
+
+ private void processUnreliableDeep(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
+ MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
+ MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
+
+ for (Statement s : unreliableLeft) {
+ subjectLeft.add(s.getSubject(),s);
+ objectLeft.add(s.getObject(),s);
+ }
+ for (Statement s : unreliableRight) {
+ subjectRight.add(s.getSubject(),s);
+ objectRight.add(s.getObject(),s);
+ }
+ for (Resource ol : objectLeft.getKeys()) {
+ Set<Path> pathsLeft = new HashSet<Path>();
+ for (Resource rel : traversed) {
+ pathsLeft.addAll(Path.create(g.getStatements(ol, rel)));
+ }
+ while (true) {
+ expand(pathsLeft);
+ if (pathsLeft.size() == 0)
+ break;
+ Collection<Path> endPaths = new ArrayList<Path>(1);
+ for (Path p : pathsLeft) {
+ if (comparableResources.containsLeft(p.getEnd())) {
+ endPaths.add(p);
+ }
+ }
+ if (endPaths.size() > 0) {
+ pathsLeft.clear();
+ pathsLeft.addAll(endPaths);
+ break;
+ }
+ }
+ if (pathsLeft.size() > 0) {
+ Resource sl = objectLeft.getValues(ol).get(0).getSubject();
+ Resource sr = comparableResources.getRight(sl);
+ Collection<Resource> possibleOR = new ArrayList<Resource>();
+ for (Statement s : subjectRight.getValues(sr)) {
+ possibleOR.add(s.getObject());
+ }
+ Map<Resource,Set<Path>> matchingPaths = new HashMap<Resource, Set<Path>>();
+ for (Resource or : possibleOR) {
+ Set<Path> possiblePathsRight = new HashSet<Path>();
+ for (Path leftPath : pathsLeft) {
+ possiblePathsRight.addAll(findComparableRight(leftPath, or));
+ }
+ if (hasMatchingPaths(pathsLeft, possiblePathsRight)) {
+ matchingPaths.put(or, possiblePathsRight);
+ }
+ }
+ if (matchingPaths.size() > 0) {
+ if (matchingPaths.size() == 1) {
+ Resource or = matchingPaths.keySet().iterator().next();
+
+ objectsLeft.add(ol);
+ objectsRight.add(or);
+ addComparable(ol, or, false);
+ Collection<Statement> statementsLeft = objectLeft.getValues(ol);
+ Collection<Statement> statementsRight = objectRight.getValues(or);
+ unreliableLeft.removeAll(statementsLeft);
+ unreliableRight.removeAll(statementsRight);
+ BijectionMap<Path,Path> map = getMatchingPaths(pathsLeft, matchingPaths.get(or));
+ for (Path left : map.getLeftSet()) {
+ Path right = map.getRight(left);
+ for (int i = 0; i < left.getLength(); i++) {
+ addComparable(left.getStatements().get(i),right.getStatements().get(i),false);
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ private boolean hasMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) {
+ if (leftPaths.size() != rightPaths.size())
+ return false;
+ BijectionMap<Path,Path> map = getMatchingPaths(leftPaths, rightPaths);
+ return map.size() == leftPaths.size();
+ }
+
+ private BijectionMap<Path,Path> getMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) {
+ BijectionMap<Path,Path> map = new BijectionMap<Path, Path>();
+ for (Path leftPath : leftPaths) {
+ for (Path rightPath : rightPaths) {
+ if (map.containsRight(rightPath))
+ continue;
+ if (leftPath.getLength() != rightPath.getLength())
+ continue;
+ if (comparableResources.contains(leftPath.getEnd(), rightPath.getEnd())) {
+ map.map(leftPath, rightPath);
+ break;
+ }
+ }
+ }
+ return map;
+ }
+
+ private void expand(Set<Path> paths) throws DatabaseException {
+ Set<Path> stepPathsLeft = new HashSet<Path>();
+ if (paths.size() == 0)
+ return;
+ int length = paths.iterator().next().getLength() + 1;
+ for (Path p : paths) {
+ for (Resource rel : traversed) {
+ stepPathsLeft.addAll(Path.expand(p,g.getStatements(p.getEnd(), rel)));
+ }
+ }
+ paths.clear();
+ for (Path p : stepPathsLeft) {
+ if (p.getLength() == length)
+ paths.add(p);
+ }
+ }
+
+ private Collection<Path> findComparableRight(Path leftPath, Resource beginRight) throws DatabaseException {
+ Set<Path> rightPaths = new HashSet<Path>();
+ rightPaths.addAll(Path.create(g.getStatements(beginRight, getRight(leftPath.getStatements().get(0).getPredicate()))));
+ for (int i = 1; i < leftPath.getLength(); i++) {
+ if (rightPaths.size() == 0)
+ return rightPaths;
+ Set<Path> stepPaths = new HashSet<Path>();
+ for (Path p : rightPaths) {
+ stepPaths.addAll(Path.expand(p, g.getStatements(p.getEnd(), getRight(leftPath.getStatements().get(i).getPredicate()))));
+ }
+ rightPaths.clear();
+ for (Path p : stepPaths)
+ if (p.getLength() == i+1)
+ rightPaths.add(p);
+ }
+ return rightPaths;
+
+ }
+
+ private Resource getRight(Resource r) {
+ if (comparableResources.containsLeft(r))
+ return comparableResources.getRight(r);
+ return r;
+ }
+
+
+
+ public BijectionMap<Statement, Statement> getComparableStatements() {
+ return comparableStatements;
+ }
+
+ public BijectionMap<Resource, Resource> getComparableResources() {
+ return comparableResources;
+ }
+
+ public GraphChanges getChanges() {
+ return new GraphChanges(r1,r2,changes1,changes2,modifications,comparableResources);
+ }
+
+ private void addComparable(Statement left, Statement right, boolean process) throws DatabaseException {
+ addComparable(left.getObject(), right.getObject(), process);
+ comparableStatements.map(left, right);
+ //comparableResources.map(left.getObject(), right.getObject());
+ }
+
+ private void addComparable(Resource left, Resource right, boolean process) throws DatabaseException {
+ if(!comparableResources.contains(left, right)) {
+ if (comparableResources.containsLeft(left)||comparableResources.containsRight(right)) {
+ throw new DatabaseException("Comparator error: Trying to map " + left + " to " + right + " while mappings " + left + " to " + comparableResources.getRight(left) + " and " + comparableResources.getLeft(right) + " to " + right + " exist.");
+ } else {
+ if (DEBUG) System.out.println(left + " = " + right);
+ comparableResources.map(left, right);
+ }
+ }
+
+ }
+
+ public List<Statement> filterAsserted(Resource r, Collection<Statement> in) throws DatabaseException {
+ List<Statement> out = new ArrayList<Statement>();
+ for (Statement s : in) {
+ if (!s.isAsserted(r))
+ out.add(s);
+
+ }
+ return out;
+ }
+
+
+
+ private String printStatement(ReadGraph graph, Statement s) throws DatabaseException {
+ return NameUtils.getSafeName(graph, s.getSubject()) + " " + NameUtils.getSafeName(graph, s.getPredicate()) + " " + NameUtils.getSafeName(graph, s.getObject());
+ }
+
+ private List<Statement> filterTraversed(List<Statement> in) throws DatabaseException {
+ return filter(traversed, in);
+ }
+
+ private List<Statement> filterNonTested(List<Statement> in) throws DatabaseException {
+ return filter(nonTested, in);
+ }
+
+ private List<Statement> filterNonTraversed(List<Statement> in) throws DatabaseException {
+ return filter(nonTraversed, in);
+ }
+
+ private List<Statement> filter(Collection<Resource> toFilter, List<Statement> in) throws DatabaseException {
+ if (toFilter.size() == 0)
+ return in;
+ List<Statement> out = new ArrayList<Statement>();
+ for (Statement s : in) {
+ boolean usable = true;
+ for (Resource r : toFilter) {
+ if (g.isSubrelationOf(s.getPredicate(),r)) {
+ usable = false;
+ break;
+ }
+ }
+ if (usable) {
+ out.add(s);
+ }
+
+ }
+ return out;
+ }
+
+
+ private void addDeletion(Statement s) {
+ if (!changes1Set.contains(s)) {
+ changes1Set.add(s);
+ changes1.add(s);
+ }
+ }
+
+ private void addAddition(Statement s) {
+ if (!changes2Set.contains(s)) {
+ changes2Set.add(s);
+ changes2.add(s);
+ }
+ }
+
+ private void addModification(Statement s1, Statement s2) {
+ Pair<Statement, Statement> mod = new Pair<Statement, Statement>(s1,s2);
+ if (!modificationsSet.contains(mod)) {
+ modificationsSet.add(mod);
+ modifications.add(mod);
+ }
+ }
+
+ public void sortStatement(List<Statement> list1, List<Statement> list2) {
+ sortStatement(list1, list2, scomp);
+ }
+
+ public void sortStatement(List<Statement> list1, List<Statement> list2, Comparator<Statement> scomp) {
+ Collections.sort(list1,scomp);
+ Collections.sort(list2,scomp);
+
+ List<Statement> sorted1 = new ArrayList<Statement>(list1.size());
+ List<Statement> sorted2 = new ArrayList<Statement>(list2.size());
+ sorted1.addAll(list1);
+ sorted2.addAll(list2);
+
+ int ss1 = 0;
+ int ss2 = 0;
+ for (int i = 0; i < list1.size(); ) {
+ Statement s1 = list1.get(i);
+ int same1 = sameRel(list1, i);
+ for (int j = 0; j < list2.size(); j++) {
+ Statement s2 = list2.get(j);
+ if (scomp.compare(s1, s2) == 0) {
+ int same2 = sameRel(list2, j);
+ copy(sorted1,ss1,list1,i,same1);
+ ss1 += same1;
+ copy(sorted2,ss2,list2,j,same2);
+ ss2 += same2;
+ break;
+ }
+ }
+ i+= same1;
+ }
+ if (ss1 < sorted1.size()) {
+ for (Statement s : list1) {
+ if (!sorted1.contains(s)) {
+ sorted1.set(ss1,s);
+ ss1++;
+ }
+ }
+ }
+ if (ss2 < sorted2.size()) {
+ for (Statement s : list2) {
+ if (!sorted2.contains(s)) {
+ sorted2.set(ss2,s);
+ ss2++;
+ }
+ }
+ }
+
+ list1.clear();
+ list2.clear();
+ list1.addAll(sorted1);
+ list2.addAll(sorted2);
+ }
+
+ public <T> void copy(List<T> to, int toIndex, List<T> from, int fromIndex, int amount) {
+ for (int i = 0; i < amount; i++) {
+ to.set(toIndex + i, from.get(fromIndex+ i));
+ }
+ }
+
+ public void sortResource(List<Resource> list1, List<Resource> list2) {
+ Collections.sort(list1,rcomp);
+ int js = 0;
+ for (int i = 0; i < list1.size(); i++) {
+ Resource s1 = list1.get(i);
+ for (int j = js; j < list2.size(); j++) {
+ Resource s2 = list2.get(j);
+ if (rcomp.compare(s1, s2) == 0) {
+ Resource t = list2.get(js);
+ list2.set(js, s2);
+ list2.set(j, t);
+ break;
+ }
+ }
+ js++;
+ }
+ }
+
+ private void compareStatements(List<Statement> ss1, List<Statement> ss2, Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {
+ sortStatement(ss1, ss2);
+
+ int i1 = 0;
+ int i2 = 0;
+
+ while (true) {
+ if (i1 >= ss1.size()) {
+ if (i2 >= ss2.size()) {
+ break;
+ } else {
+ while (i2 < ss2.size()) {
+ if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i2)));
+
+ addAddition(ss2.get(i2));
+ i2++;
+ }
+ break;
+ }
+ } else if (i2 >= ss2.size()) {
+ while (i1 < ss1.size()) {
+ if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i1)));
+ addDeletion(ss1.get(i1));
+ i1++;
+ }
+ break;
+ }
+ int same1 = sameRel(ss1, i1);
+ int same2 = sameRel(ss2, i2);
+ int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());
+ if (c == 0) {
+ compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight);
+ i1+=same1;
+ i2+=same2;
+ } else if (c < 0) {
+ for (int i = 0; i < same1; i++) {
+ if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i+i1)));
+ addDeletion(ss1.get(i+i1));
+ }
+ i1 += same1;
+ } else {
+ for (int i = 0; i < same2; i++) {
+ if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i+i2)));
+ addAddition(ss2.get(i+i2));
+ }
+
+ i2 += same2;
+ }
+ }
+ }
+
+
+
+ private int sameRel(List<Statement> statements, int off) {
+ if (statements.size() <= off)
+ return 0;
+ int same = 1;
+ long id = statements.get(off).getPredicate().getResourceId();
+ for (int i = off+1; i <statements.size(); i++) {
+ if (statements.get(i).getPredicate().getResourceId() == id)
+ same++;
+ else
+ break;
+ }
+ return same;
+
+ }
+
+ private int compareObject(Resource o1, Resource o2) throws DatabaseException {
+ if (o1.equals(o2))
+ return -1;
+ if (comparableResources.contains(o1, o2))
+ return (-1);
+ if (comparableResources.containsLeft(o1))
+ return Integer.MAX_VALUE;
+ if (comparableResources.containsRight(o2))
+ return Integer.MAX_VALUE;
+ if (nonMatchedLeft.contains(o1))
+ return Integer.MAX_VALUE;
+ if (nonMatchedRight.contains(o2))
+ return Integer.MAX_VALUE;
+ return comparator.compare(g, o1, o2);
+ }
+
+ private void compareStatements(List<Statement> ss1, int off1, int len1, List<Statement> ss2, int off2, int len2, Collection<Resource> objectsLeft, Collection<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {
+ boolean[] used1 = new boolean[len1];
+ for (int i = 0; i < used1.length; i++) {
+ used1[i] = false;
+ }
+
+ boolean[] used2 = new boolean[len2];
+ for (int i = 0; i < used2.length; i++) {
+ used2[i] = false;
+ }
+
+ // left, right, difference
+ List<List<Integer>> differences = new ArrayList<List<Integer>>();
+ for (int i1 = off1; i1 < off1 + len1; i1++) {
+ Statement s1 = ss1.get(i1);
+ List<Integer> diff = new ArrayList<Integer>();
+ for (int i2 = off2; i2 < off2 + len2; i2++) {
+ Statement s2 = ss2.get(i2);
+ int d = compareObject(s1.getObject(), s2.getObject());
+ if (d == 0) {
+ for (Resource t : strong) {
+ if (s1.getPredicate().equals(t) || g.isSubrelationOf(s1.getPredicate(), t)) {
+ d = 1;
+ break;
+ }
+ }
+ }
+ diff.add(d);
+ }
+ differences.add(diff);
+ }
+ // difference, left
+ MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();
+ for (int i = 0; i < differences.size(); i++) {
+ List<Integer> list = differences.get(i);
+ for (int j = 0; j < list.size(); j++) {
+ priorities.add(list.get(j), i);
+ }
+ }
+
+ Integer[] pris = priorities.getKeys(new Integer[]{});
+ Arrays.sort(pris);
+
+ for (Integer pri : pris) {
+ if (pri == Integer.MAX_VALUE) {
+
+ } else if (pri == 0) {
+
+ } else {
+ List<Integer> i1s = priorities.getValues(pri);
+ for (Integer i1 : i1s) {
+ if (used1[i1])
+ continue;
+ List<Integer> i2diff = differences.get(i1);
+ for (int i2 = 0; i2 < i2diff.size(); i2++) {
+ if (i2diff.get(i2) == pri) {
+ if (used2[i2])
+ continue;
+ used1[i1] = true;
+ used2[i2] = true;
+ Statement s1 = ss1.get(i1+off1);
+ Statement s2 = ss2.get(i2+off2);
+
+ if (objectsLeft != null) {
+ objectsLeft.add(s1.getObject());
+ objectsRight.add(s2.getObject());
+ }
+ addComparable(s1, s2, true);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (Integer pri : pris) {
+ if (pri != 0)
+ continue;
+ Set<Statement> s1s = new HashSet<Statement>();
+ Set<Statement> s2s = new HashSet<Statement>();
+ Set<Integer> s1i = new HashSet<Integer>();
+ Set<Integer> s2i = new HashSet<Integer>();
+ List<Integer> i1s = priorities.getValues(pri);
+ for (Integer i1 : i1s) {
+ if (used1[i1])
+ continue;
+ List<Integer> i2diff = differences.get(i1);
+ for (int i2 = 0; i2 < i2diff.size(); i2++) {
+ if (i2diff.get(i2) == pri) {
+ if (used2[i2])
+ continue;
+ Statement s1 = ss1.get(i1+off1);
+ Statement s2 = ss2.get(i2+off2);
+ s1s.add(s1);
+ s2s.add(s2);
+ s1i.add(i1);
+ s2i.add(i2);
+ }
+ }
+ }
+ if (unreliableLeft != null) {
+ unreliableLeft.addAll(s1s);
+ unreliableRight.addAll(s2s);
+ }
+ for (Integer i : s1i)
+ used1[i] = true;
+ for (Integer i : s2i)
+ used2[i] = true;
+
+ }
+ for (int i1 = off1; i1 < off1 + len1; i1++) {
+ if (!used1[i1-off1]) {
+ if (DEBUG) System.out.println("Compare Object deletion " + printStatement(g,ss1.get(i1)));
+ addDeletion(ss1.get(i1));
+ }
+ }
+ for (int i2 = off2; i2 < off2 + len2; i2++) {
+ if (!used2[i2-off2]) {
+ if (DEBUG) System.out.println("Compare Object addition " + printStatement(g,ss2.get(i2)));
+ addAddition(ss2.get(i2));
+ }
+ }
+ }
+
+
+
+ /**
+ * compares properties, assumes functional relations
+ * @param r1
+ * @param r2
+ * @throws ServiceException
+ * @throws DoesNotContainValueException
+ * @throws ValidationException
+ */
+ private void compareProps(Resource r1, Resource r2) throws DatabaseException {
+ if (DEBUG) System.out.println("compareProps " + r1 + " " + NameUtils.getSafeName(g, r1) + " " + r2 + " " + NameUtils.getSafeName(g, r2));
+ List<Statement> ss1 = new ArrayList<Statement>();
+ List<Statement> ss2 = new ArrayList<Statement>();
+ ss1.addAll(g.getStatements(r1, b.HasProperty));
+ ss2.addAll(g.getStatements(r2, b.HasProperty));
+ ss1 = filterNonTested(ss1);
+ ss2 = filterNonTested(ss2);
+ sortStatement(ss1, ss2);
+
+ int i1 = 0;
+ int i2 = 0;
+
+ while (true) {
+ if (i1 >= ss1.size()) {
+ if (i2 >= ss2.size())
+ break;
+ else {
+ while (i2 < ss2.size()) {
+ if (DEBUG) System.out.println("Compare Prop diff2 " + printStatement(g,ss2.get(i2)));
+ addAddition(ss2.get(i2));
+ i2++;
+ }
+ break;
+ }
+ } else if (i2 >= ss2.size()) {
+ while (i1 < ss1.size()) {
+ if (DEBUG) System.out.println("Compare Prop diff1 " + printStatement(g,ss1.get(i1)));
+ addDeletion(ss1.get(i1));
+ i1++;
+ }
+ break;
+ }
+ Statement s1 = ss1.get(i1);
+ Statement s2 = ss2.get(i2);
+ if (s1.isAsserted(r1) && s2.isAsserted(r2)) {
+ i1++;
+ i2++;
+ continue;
+ }
+ int c = scomp.compare(s1, s2);
+ switch (c) {
+ case 0:{
+ boolean b1 = g.hasValue(s1.getObject());
+ boolean b2 = g.hasValue(s2.getObject());
+ if (b1 == b2) {
+ if (b1) {
+ Object v1 = g.getValue(s1.getObject());
+ Object v2 = g.getValue(s2.getObject());
+ boolean eq = compareValue(v1, v2);
+ if (!eq) {
+ addModification(s1, s2);
+ addComparable(s1, s2, false);
+ }
+ } else {
+ if (!s1.getObject().equals(s1.getSubject()) && !s2.getObject().equals(s2.getSubject()))
+ compareProps(s1.getObject(), s2.getObject());
+ }
+ } else {
+ addModification(s1, s2);
+ addComparable(s1, s2, false);
+ }
+ i1++;
+ i2++;
+ break;
+ }
+ case -1:{
+ if (DEBUG) System.out.println("Compare Prop diff1s " + printStatement(g,s1));
+ addDeletion(s1);
+ i1++;
+ break;
+ }
+
+ case 1:{
+ if (DEBUG) System.out.println("Compare Prop diff2s " + printStatement(g,s2));
+ addAddition(s2);
+ i2++;
+ break;
+ }
+ }
+
+ }
+
+ ss1.clear();
+ ss2.clear();
+
+ }
+
+ public static boolean compareValue(Object v1, Object v2) {
+ if (v1 instanceof Object[] && v2 instanceof Object[])
+ return Arrays.deepEquals((Object[])v1, (Object[])v2);
+ else if (v1 instanceof int[] && v2 instanceof int[])
+ return Arrays.equals((int[])v1, (int[])v2);
+ else if (v1 instanceof float[] && v2 instanceof float[])
+ return Arrays.equals((float[])v1, (float[])v2);
+ else if (v1 instanceof double[] && v2 instanceof double[])
+ return Arrays.equals((double[])v1, (double[])v2);
+ else if (v1 instanceof long[] && v2 instanceof long[])
+ return Arrays.equals((long[])v1, (long[])v2);
+ else if (v1 instanceof byte[] && v2 instanceof byte[])
+ return Arrays.equals((byte[])v1, (byte[])v2);
+ else if (v1 instanceof boolean[] && v2 instanceof boolean[])
+ return Arrays.equals((boolean[])v1, (boolean[])v2);
+ else
+ return v1.equals(v2);
+ }
+
+
+ public class PredicateComparator implements Comparator<Statement> {
+ @Override
+ public int compare(Statement o1, Statement o2) {
+ if (comparableResources.contains(o1.getPredicate(), o2.getPredicate()))
+ return 0;
+ if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
+ return -1;
+ if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
+ return 1;
+ return 0;
+ }
+ }
+
+ public class SubjectComparator implements Comparator<Statement> {
+ @Override
+ public int compare(Statement o1, Statement o2) {
+ if (comparableResources.contains(o1.getSubject(), o2.getSubject()))
+ return 0;
+ if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
+ return -1;
+ if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
+ return 1;
+ return 0;
+ }
+ }
+
+ public class ObjectComparator implements Comparator<Statement> {
+ @Override
+ public int compare(Statement o1, Statement o2) {
+ if (comparableResources.contains(o1.getObject(), o2.getObject()))
+ return 0;
+ if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
+ return -1;
+ if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
+ return 1;
+ return 0;
+ }
+ }
+
+ public static class FullStatementComparator implements Comparator<Statement> {
+ @Override
+ public int compare(Statement o1, Statement o2) {
+ if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
+ return -1;
+ if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
+ return 1;
+ if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
+ return -1;
+ if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
+ return 1;
+ if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
+ return -1;
+ if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
+ return 1;
+ return 0;
+ }
+ }
+
+ public class ResComparator implements Comparator<Resource> {
+ @Override
+ public int compare(Resource o1, Resource o2) {
+ if (comparableResources.contains(o1, o2))
+ return 0;
+ if (o1.getResourceId() < o2.getResourceId())
+ return -1;
+ if (o1.getResourceId() > o2.getResourceId())
+ return 1;
+ return 0;
+ }
+ }
+
+}