1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Foster Wheeler Energia Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.interop.test;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
23 import java.util.Map.Entry;
25 import java.util.Stack;
27 import org.simantics.databoard.Bindings;
28 import org.simantics.db.ReadGraph;
29 import org.simantics.db.Resource;
30 import org.simantics.db.Session;
31 import org.simantics.db.Statement;
32 import org.simantics.db.common.request.ReadRequest;
33 import org.simantics.db.common.utils.NameUtils;
34 import org.simantics.db.exception.DatabaseException;
35 import org.simantics.interop.test.GraphChanges.Modification;
36 import org.simantics.layer0.Layer0;
37 import org.simantics.utils.datastructures.BijectionMap;
38 import org.simantics.utils.datastructures.MapList;
39 import org.simantics.utils.datastructures.Pair;
42 * Compares two subgraphs and reports differences.
44 * Assumes that subgraphs (defined using traverse relations) are not cyclic.
46 * Assumes that properties can be used to identify objects, if relation type is not enough.
50 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
53 public class GraphComparator {
55 private static final boolean DEBUG = false;
59 private Set<Resource> strong = new HashSet<>(); // List of relations that identify object, if subject is already identified.
60 private List<Resource> traversed = new ArrayList<>(); // list of relations that are traversed (and tested)
61 private List<Resource> tested = new ArrayList<>(); // list of relations that are tested, but not traversed
62 private List<Resource> nonTraversed = new ArrayList<>(); // list of relations that are not traversed
63 private List<Resource> nonTested = new ArrayList<>(); // list of relations that are not tested
65 private List<Statement> changes1 = new ArrayList<>();
66 private List<Statement> changes2 = new ArrayList<>();
67 private List<Modification> modifications = new ArrayList<>();
68 private Set<Statement> changes1Set = new HashSet<>();
69 private Set<Statement> changes2Set = new HashSet<>();
70 private Set<Modification> modificationsSet = new HashSet<>();
72 private BijectionMap<Statement, Statement> comparableStatements = new BijectionMap<>();
73 private BijectionMap<Resource, Resource> comparableResources = new BijectionMap<>();
75 private Set<Resource> processedResources = new HashSet<Resource>();
77 private ResourceComparator comparator;
79 private Comparator<Statement> scomp = new PredicateComparator();
80 private Comparator<Resource> rcomp = new ResComparator();
82 private Set<Resource> nonMatchedLeft = new HashSet<Resource>();
83 private Set<Resource> nonMatchedRight = new HashSet<Resource>();
90 public GraphComparator(Resource r1, Resource r2) {
93 comparator = new TypeComparator();
96 public GraphComparator(Resource r1, Resource r2, ResourceComparator comparator) {
99 this.comparator = comparator;
102 ArrayList<Statement> ss1 = new ArrayList<Statement>();
103 ArrayList<Statement> ss2 = new ArrayList<Statement>();
106 public Comparator<Resource> getResourceComparator() {
110 public Comparator<Statement> getStatementComparator() {
114 public Resource getR1() {
118 public Resource getR2() {
122 public void addTraversed(Resource rel) {
126 public void addTraversed(Collection<Resource> rels) {
127 traversed.addAll(rels);
130 public void addNonTraversed(Resource rel) {
131 nonTraversed.add(rel);
134 public void addNonTraversed(Collection<Resource> rels) {
135 nonTraversed.addAll(rels);
138 public void addTested(Resource rel) {
142 public void addTested(Collection<Resource> rels) {
146 public void addNonTested(Resource rel) {
150 public void addNonTested(Collection<Resource> rels) {
151 nonTested.addAll(rels);
154 public void addComparableResources(Resource r1, Resource r2) {
156 System.out.println("Preset " + r1 + " = " + r2);
157 comparableResources.map(r1, r2);
160 public void addComparableResources(BijectionMap<Resource, Resource> matching) {
162 for (Entry<Resource, Resource> entry : matching.getEntries())
163 System.out.println("Preset " + entry.getKey() + " = " + entry.getValue());
165 comparableResources.addAll(matching);
168 public void addStrong(Resource r) {
172 public void addStrong(Collection<Resource> rels) {
176 public void addNonMatchedLeft(Resource r) {
177 nonMatchedLeft.add(r);
180 public void addNonMatchedRight(Resource r) {
181 nonMatchedRight.add(r);
184 public void test(ReadGraph g) throws DatabaseException {
186 this.b = Layer0.getInstance(g);
187 comparator.setComparator(this);
188 comparator.initialize(g, r1, r2);
190 Stack<Resource> objectsLeft = new Stack<Resource>();
191 Stack<Resource> objectsRight = new Stack<Resource>();
192 objectsLeft.push(r1);
193 objectsRight.push(r2);
195 Set<Statement> unreliableLeft = new HashSet<Statement>();
196 Set<Statement> unreliableRight = new HashSet<Statement>();
199 if (objectsLeft.isEmpty())
203 // process compares objects that are identified and searches for more resources to process.
204 process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
205 // process unreliable handles cases where unidentified statements subject and object have been identified
206 processUnreliable(unreliableLeft, unreliableRight);
207 // process unreliable handles cases where unidentified resources have path of length one to identified resource
208 processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
209 if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
210 processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
212 if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
213 // comparison is ending, but we have still unprocessed unidentified resources left.
214 // These cases have longer path than one to identified objects.
215 processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
219 for (Statement s : unreliableLeft) {
220 if (!comparableStatements.containsLeft(s))
223 for (Statement s : unreliableRight) {
224 if (!comparableStatements.containsRight(s))
229 public void test(Session session) throws DatabaseException {
230 test(session, r1, r2);
233 public void test(Session session, Resource r1, Resource r2) throws DatabaseException {
235 comparator.setComparator(this);
237 session.syncRequest(new ReadRequest() {
240 public void run(ReadGraph graph) throws DatabaseException {
241 comparator.initialize(graph, r1, r2);
245 addComparable(r1, r2);
247 final Stack<Resource> objectsLeft = new Stack<Resource>();
248 final Stack<Resource> objectsRight = new Stack<Resource>();
249 objectsLeft.push(r1);
250 objectsRight.push(r2);
252 final Set<Statement> unreliableLeft = new HashSet<Statement>();
253 final Set<Statement> unreliableRight = new HashSet<Statement>();
256 if (objectsLeft.isEmpty())
258 session.syncRequest(new ReadRequest() {
261 public void run(ReadGraph graph) throws DatabaseException {
263 b = Layer0.getInstance(graph);
264 // process compares objects that are identified and searches for more resources to process.
265 process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
266 // process unreliable handles cases where unidentified statements subject and object have been identified
267 processUnreliable(unreliableLeft, unreliableRight);
268 // process unreliable handles cases where unidentified resources have path of length one to identified resource
269 processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
270 if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
271 processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
273 if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
274 // comparison is ending, but we have still unprocessed unidentified resources left.
275 // These cases have longer path than one to identified objects.
276 processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
278 if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
279 // comparison is ending, but we have still unprocessed unidentified resources left.
280 // These cases have longer path than one to identified objects.
281 processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
289 for (Statement s : unreliableLeft) {
290 if (!comparableStatements.containsLeft(s))
293 for (Statement s : unreliableRight) {
294 if (!comparableStatements.containsRight(s))
301 private void process(Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
302 List<Statement> ss1 = new ArrayList<Statement>();
303 List<Statement> ss2 = new ArrayList<Statement>();
305 while (!objectsLeft.isEmpty()) {
306 Resource r1 = objectsLeft.pop();
307 Resource r2 = objectsRight.pop();
311 if (processedResources.contains(r1))
313 processedResources.add(r1);
316 if((comparableResources.containsLeft(r1)||comparableResources.containsRight(r2)) && !comparableResources.contains(r1, r2)) {
317 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.");
319 addComparable(r1, r2);
321 //System.out.println("test " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));
322 compareProps(r1, r2);
324 for (Resource rel : tested) {
325 ss1.addAll(g.getStatements(r1, rel));
326 ss2.addAll(g.getStatements(r2, rel));
327 ss1 = filterAsserted(r1, ss1);
328 ss2 = filterAsserted(r2, ss2);
329 ss1 = filterTraversed(ss1);
330 ss2 = filterTraversed(ss2);
331 ss1 = filterNonTested(ss1);
332 ss2 = filterNonTested(ss2);
335 compareStatements(ss1, ss2, null, null,null,null);
340 for (Resource rel : traversed) {
341 ss1.addAll(g.getStatements(r1, rel));
342 ss2.addAll(g.getStatements(r2, rel));
343 ss1 = filterAsserted(r1, ss1);
344 ss2 = filterAsserted(r2, ss2);
345 ss1 = filterNonTraversed(ss1);
346 ss2 = filterNonTraversed(ss2);
347 compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
355 private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
356 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
357 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
358 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
359 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
361 for (Statement s : unreliableLeft) {
362 subjectLeft.add(s.getSubject(),s);
363 objectLeft.add(s.getObject(),s);
365 for (Statement s : unreliableRight) {
366 subjectRight.add(s.getSubject(),s);
367 objectRight.add(s.getObject(),s);
370 for (Resource left : subjectLeft.getKeys()) {
371 Resource right = comparableResources.getRight(left);
374 for (Statement leftS : subjectLeft.getValues(left)) {
375 Resource leftO = leftS.getObject();
376 if (!unreliableLeft.contains(leftS))
378 Resource rightO = comparableResources.getRight(leftO);
381 for (Statement rightS : subjectRight.getValues(right)) {
382 if (!rightS.getObject().equals(rightO))
384 if (!unreliableRight.contains(rightS))
386 if (leftS.getPredicate().equals(rightS.getPredicate()) ||
387 comparableResources.contains(leftS.getPredicate(), rightS.getPredicate())) {
388 unreliableLeft.remove(leftS);
389 unreliableRight.remove(rightS);
390 addComparable(leftS, rightS);
397 private void processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
398 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
399 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
400 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
401 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
403 for (Statement s : unreliableLeft) {
404 subjectLeft.add(s.getSubject(),s);
405 objectLeft.add(s.getObject(),s);
407 for (Statement s : unreliableRight) {
408 subjectRight.add(s.getSubject(),s);
409 objectRight.add(s.getObject(),s);
412 for (Resource ol : objectLeft.getKeys()) {
413 // all statements to the left side object
414 List<Statement> left = objectLeft.getValues(ol);
415 // all subjects that have statements to the left side object (ol)
416 Set<Resource> sLeft = new HashSet<Resource>();
417 // all matching subjects on the right side
418 Set<Resource> sRight = new HashSet<Resource>();
419 for (Statement s : left) {
420 sLeft.add(s.getSubject());
421 sRight.add(comparableResources.getRight(s.getSubject()));
424 // check if object left can be reliably identified by available statements
425 // if there are any objects on the left side with similar statements, object left cannot be mapped.
426 boolean hasSimilar = false;
427 MapList<Resource, Statement> comparableOLeft = new MapList<Resource, Statement>();
428 for (Resource sl : sLeft) {
429 for (Statement s : subjectLeft.getValues(sl)) {
430 if (!s.getObject().equals(ol)) {
431 comparableOLeft.add(s.getObject(),s);
436 compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
438 for (Resource similarOl : comparableOLeft.getKeys()) {
439 List<Statement> similarLeft = comparableOLeft.getValues(similarOl);
440 if (similarLeft.size() == left.size()) {
441 boolean useL[] = new boolean[left.size()];
442 boolean useSL[] = new boolean[left.size()];
443 for (int i = 0; i < left.size(); i++) {
447 for (int i = 0; i < left.size(); i++) {
448 for (int j = 0; j < left.size(); j++) {
451 // compare predicates
452 Resource pl = left.get(i).getPredicate();
453 Resource psl = similarLeft.get(j).getPredicate();
454 if (pl.equals(psl)) {
455 // compare objects (unreliable result is interpreted as positive match)
457 int comp = comparator.compare(g, left.get(i).getObject(), similarLeft.get(j).getObject(), true);
458 if (comp >= 0 && comp < ResourceComparator.NO_MATCH) {
466 boolean diff = false;
467 for (int i = 0; i < left.size(); i++) {
468 if (!useL[i] || !useSL[i]) {
483 // all objects that subjects on the right side point to. Object left has its matching resource among these, if it has matching resource
484 MapList<Resource,Statement> possibleOR = new MapList<Resource, Statement>();
485 for (Resource sr : sRight) {
486 for (Statement s : subjectRight.getValues(sr))
487 possibleOR.add(s.getObject(),s);
490 // filter possible right side objects to those that have same amount of statements as the left side object
491 for (Resource or : possibleOR.getKeys().toArray(new Resource[possibleOR.getKeys().size()])) {
492 List<Statement> right = possibleOR.getValues(or);
493 if (right.size() != left.size())
494 possibleOR.remove(or);
498 // check for matching statements (comparable subjects, matching predicates)
499 MapList<Resource,Statement> matchingOR = new MapList<Resource, Statement>(); // list of objects that have matching statements
500 Map<Resource,Pair<int[], int[]>> matchingStatements = new HashMap<Resource, Pair<int[], int[]>>(); // matching statements
501 for (Resource or : possibleOR.getKeys()) {
502 List<Statement> right = possibleOR.getValues(or);
503 int iLeft[] = new int[left.size()];
504 int iRight[] = new int[right.size()];
506 for (int i = 0; i < left.size(); i++) {
511 for (int l = 0; l < left.size(); l++) {
512 Statement ls = left.get(l);
513 for (int r = 0; r < right.size(); r++) {
516 Statement rs = right.get(r);
517 if (!comparableResources.contains(ls.getSubject(), rs.getSubject()))
519 if ((comparableResources.containsLeft(ls.getObject()) || comparableResources.containsRight(rs.getObject())) && !comparableResources.contains(ls.getObject(), rs.getObject()))
521 if (rcomp.compare(ls.getPredicate(),rs.getPredicate()) == 0) {
522 // compare objects (unreliable result is not accepted)
523 int comp = comparator.compare(g, ls.getObject(), rs.getObject());
524 if (comp > 0 && comp < Integer.MAX_VALUE) {
534 boolean success = true;
535 for (int i = 0; i < left.size(); i++) {
547 for (Statement s : right)
548 matchingOR.add(or,s);
549 matchingStatements.put(or, new Pair<int[], int[]>(iLeft, iRight));
552 // if there is only one matching right side object, we have found a match
553 if (matchingOR.getKeySize() == 1) {
554 Resource or = matchingOR.getKeys().iterator().next();
555 List<Statement> right = matchingOR.getValues(or);
556 Pair<int[], int[]> indices = matchingStatements.get(or);
559 objectsRight.add(or);
560 addComparable(ol, or);
561 for (int l = 0; l < left.size(); l++) {
562 int r = indices.first[l];
563 Statement sl = left.get(l);
564 Statement sr = right.get(r);
565 addComparable(sl, sr);
566 unreliableLeft.remove(sl);
567 unreliableRight.remove(sr);
579 private void processUnreliable2(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
580 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
581 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
582 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
583 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
585 for (Statement s : unreliableLeft) {
586 subjectLeft.add(s.getSubject(),s);
587 objectLeft.add(s.getObject(),s);
589 for (Statement s : unreliableRight) {
590 subjectRight.add(s.getSubject(),s);
591 objectRight.add(s.getObject(),s);
594 for (Resource ol : objectLeft.getKeys()) {
595 // all statements to the left side object
596 List<Statement> left = objectLeft.getValues(ol);
597 // all subjects that have statements to the left side object (ol)
598 Set<Resource> sLeft = new HashSet<Resource>();
599 // all matching subjects on the right side
600 Set<Resource> sRight = new HashSet<Resource>();
601 for (Statement s : left) {
602 sLeft.add(s.getSubject());
603 sRight.add(comparableResources.getRight(s.getSubject()));
606 if (sLeft.size() == 1 && sRight.size() == 1) {
607 List<Statement> ss1 = new ArrayList<Statement>(subjectLeft.getValues(sLeft.iterator().next()));
608 List<Statement> ss2 = new ArrayList<Statement>(subjectRight.getValues(sRight.iterator().next()));
610 int count = comparableStatements.size();
611 compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
612 if (comparableStatements.size() > count) {
613 for (Entry<Statement, Statement> entry : comparableStatements.getEntries()) {
614 unreliableLeft.remove(entry.getKey());
615 unreliableRight.remove(entry.getValue());
622 private void processUnreliableDeep(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
623 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
624 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
625 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
626 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
628 for (Statement s : unreliableLeft) {
629 subjectLeft.add(s.getSubject(),s);
630 objectLeft.add(s.getObject(),s);
632 for (Statement s : unreliableRight) {
633 subjectRight.add(s.getSubject(),s);
634 objectRight.add(s.getObject(),s);
636 for (Resource ol : objectLeft.getKeys()) {
637 Set<Path> pathsLeft = new HashSet<Path>();
638 for (Resource rel : traversed) {
639 pathsLeft.addAll(Path.create(g.getStatements(ol, rel)));
643 if (pathsLeft.size() == 0)
645 Collection<Path> endPaths = new ArrayList<Path>(1);
646 for (Path p : pathsLeft) {
647 if (comparableResources.containsLeft(p.getEnd())) {
651 if (endPaths.size() > 0) {
653 pathsLeft.addAll(endPaths);
657 if (pathsLeft.size() > 0) {
658 Resource sl = objectLeft.getValues(ol).get(0).getSubject();
659 Resource sr = comparableResources.getRight(sl);
660 Collection<Resource> possibleOR = new ArrayList<Resource>();
661 for (Statement s : subjectRight.getValues(sr)) {
662 possibleOR.add(s.getObject());
664 Map<Resource,Set<Path>> matchingPaths = new HashMap<Resource, Set<Path>>();
665 for (Resource or : possibleOR) {
666 Set<Path> possiblePathsRight = new HashSet<Path>();
667 for (Path leftPath : pathsLeft) {
668 possiblePathsRight.addAll(findComparableRight(leftPath, or));
670 if (hasMatchingPaths(pathsLeft, possiblePathsRight)) {
671 matchingPaths.put(or, possiblePathsRight);
674 if (matchingPaths.size() > 0) {
675 if (matchingPaths.size() == 1) {
676 Resource or = matchingPaths.keySet().iterator().next();
679 objectsRight.add(or);
680 addComparable(ol, or);
681 Collection<Statement> statementsLeft = objectLeft.getValues(ol);
682 Collection<Statement> statementsRight = objectRight.getValues(or);
683 unreliableLeft.removeAll(statementsLeft);
684 unreliableRight.removeAll(statementsRight);
685 BijectionMap<Path,Path> map = getMatchingPaths(pathsLeft, matchingPaths.get(or));
686 for (Path left : map.getLeftSet()) {
687 Path right = map.getRight(left);
688 for (int i = 0; i < left.getLength(); i++) {
689 addComparable(left.getStatements().get(i),right.getStatements().get(i));
700 private boolean hasMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) throws DatabaseException {
701 if (leftPaths.size() != rightPaths.size())
703 BijectionMap<Path,Path> map = getMatchingPaths(leftPaths, rightPaths);
704 return map.size() == leftPaths.size();
707 private BijectionMap<Path,Path> getMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) throws DatabaseException {
708 BijectionMap<Path,Path> map = new BijectionMap<Path, Path>();
709 for (Path leftPath : leftPaths) {
710 for (Path rightPath : rightPaths) {
711 if (map.containsRight(rightPath))
713 if (leftPath.getLength() != rightPath.getLength())
715 if (comparableResources.contains(leftPath.getEnd(), rightPath.getEnd())) {
716 boolean match = true;
717 for (int i = 0; i < leftPath.getLength(); i++) {
718 Statement sl = leftPath.getStatements().get(i);
719 Statement sr = rightPath.getStatements().get(i);
720 if (!sl.getPredicate().equals(sr.getPredicate()) && !comparableResources.contains(sl.getPredicate(), sr.getPredicate())) {
724 if ((getComparableResources().containsLeft(sl.getObject()) || getComparableResources().containsRight(sr.getObject())) && !getComparableResources().contains(sl.getObject(), sr.getObject())) {
728 if (comparator.compare(g, sl.getObject(), sr.getObject()) == ResourceComparator.NO_MATCH) {
734 map.map(leftPath, rightPath);
743 private void expand(Set<Path> paths) throws DatabaseException {
744 Set<Path> stepPathsLeft = new HashSet<Path>();
745 if (paths.size() == 0)
747 int length = paths.iterator().next().getLength() + 1;
748 for (Path p : paths) {
749 for (Resource rel : traversed) {
750 stepPathsLeft.addAll(Path.expand(p,g.getStatements(p.getEnd(), rel)));
754 for (Path p : stepPathsLeft) {
755 if (p.getLength() == length)
760 private Collection<Path> findComparableRight(Path leftPath, Resource beginRight) throws DatabaseException {
761 Set<Path> rightPaths = new HashSet<Path>();
762 rightPaths.addAll(Path.create(g.getStatements(beginRight, getRight(leftPath.getStatements().get(0).getPredicate()))));
763 for (int i = 1; i < leftPath.getLength(); i++) {
764 if (rightPaths.size() == 0)
766 Set<Path> stepPaths = new HashSet<Path>();
767 for (Path p : rightPaths) {
768 stepPaths.addAll(Path.expand(p, g.getStatements(p.getEnd(), getRight(leftPath.getStatements().get(i).getPredicate()))));
771 for (Path p : stepPaths)
772 if (p.getLength() == i+1)
779 private Resource getRight(Resource r) {
780 if (comparableResources.containsLeft(r))
781 return comparableResources.getRight(r);
787 public BijectionMap<Statement, Statement> getComparableStatements() {
788 return comparableStatements;
791 public BijectionMap<Resource, Resource> getComparableResources() {
792 return comparableResources;
795 public GraphChanges getChanges() {
796 return new GraphChanges(r1,r2,changes1,changes2,modifications,comparableResources);
799 public List<Statement> getChanges1() {
803 public List<Statement> getChanges2() {
807 private void addComparable(Statement left, Statement right) throws DatabaseException {
808 addComparable(left.getObject(), right.getObject());
809 comparableStatements.map(left, right);
810 //comparableResources.map(left.getObject(), right.getObject());
813 private void addComparable(Resource left, Resource right) throws DatabaseException {
814 if(!comparableResources.contains(left, right)) {
815 if (comparableResources.containsLeft(left)||comparableResources.containsRight(right)) {
816 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.");
818 if (DEBUG) System.out.println(left + " = " + right);
819 comparableResources.map(left, right);
825 public List<Statement> filterAsserted(Resource r, Collection<Statement> in) throws DatabaseException {
826 List<Statement> out = new ArrayList<Statement>();
827 for (Statement s : in) {
828 if (!s.isAsserted(r))
835 public List<Statement> filterAssertedDuplicates(Resource r, List<Statement> in) throws DatabaseException {
836 List<Statement> out = new ArrayList<Statement>();
837 for (int i = 0; i < in.size(); i++) {
838 Statement s = in.get(i);
839 if (!s.isAsserted(r))
843 if (i > 0 && in.get(i-1).getPredicate().equals(s.getPredicate()))
845 else if (i < in.size()-1 && in.get(i+1).getPredicate().equals(s.getPredicate()))
857 private String printStatement(ReadGraph graph, Statement s) throws DatabaseException {
858 return NameUtils.getSafeName(graph, s.getSubject()) + " " + NameUtils.getSafeName(graph, s.getPredicate()) + " " + NameUtils.getSafeName(graph, s.getObject());
861 private List<Statement> filterTraversed(List<Statement> in) throws DatabaseException {
862 return filter(traversed, in);
865 private List<Statement> filterNonTested(List<Statement> in) throws DatabaseException {
866 return filter(nonTested, in);
869 private List<Statement> filterNonTraversed(List<Statement> in) throws DatabaseException {
870 return filter(nonTraversed, in);
873 private List<Statement> filter(Collection<Resource> toFilter, List<Statement> in) throws DatabaseException {
874 if (toFilter.size() == 0)
876 List<Statement> out = new ArrayList<Statement>();
877 for (Statement s : in) {
878 boolean usable = true;
879 for (Resource r : toFilter) {
880 if (g.isSubrelationOf(s.getPredicate(),r)) {
894 private void addDeletion(Statement s) {
895 if (!changes1Set.contains(s)) {
901 private void addAddition(Statement s) {
902 if (!changes2Set.contains(s)) {
908 private void addModification(Resource left, Statement leftstm, Resource right, Statement rightstm) {
909 Modification mod = new Modification(left, right, leftstm, rightstm);
910 if (!modificationsSet.contains(mod)) {
911 modificationsSet.add(mod);
912 modifications.add(mod);
916 public void sortStatement(List<Statement> list1, List<Statement> list2) {
917 sortStatement(list1, list2, scomp);
920 public void sortStatement(List<Statement> list1, List<Statement> list2, Comparator<Statement> scomp) {
921 Collections.sort(list1,scomp);
922 Collections.sort(list2,scomp);
924 List<Statement> sorted1 = new ArrayList<Statement>(list1.size());
925 List<Statement> sorted2 = new ArrayList<Statement>(list2.size());
926 sorted1.addAll(list1);
927 sorted2.addAll(list2);
931 for (int i = 0; i < list1.size(); ) {
932 Statement s1 = list1.get(i);
933 int same1 = sameRel(list1, i);
934 for (int j = 0; j < list2.size(); j++) {
935 Statement s2 = list2.get(j);
936 if (scomp.compare(s1, s2) == 0) {
937 int same2 = sameRel(list2, j);
938 copy(sorted1,ss1,list1,i,same1);
940 copy(sorted2,ss2,list2,j,same2);
947 if (ss1 < sorted1.size()) {
948 for (Statement s : list1) {
949 if (!sorted1.contains(s)) {
955 if (ss2 < sorted2.size()) {
956 for (Statement s : list2) {
957 if (!sorted2.contains(s)) {
966 list1.addAll(sorted1);
967 list2.addAll(sorted2);
970 public <T> void copy(List<T> to, int toIndex, List<T> from, int fromIndex, int amount) {
971 for (int i = 0; i < amount; i++) {
972 to.set(toIndex + i, from.get(fromIndex+ i));
976 public void sortResource(List<Resource> list1, List<Resource> list2) {
977 Collections.sort(list1,rcomp);
979 for (int i = 0; i < list1.size(); i++) {
980 Resource s1 = list1.get(i);
981 for (int j = js; j < list2.size(); j++) {
982 Resource s2 = list2.get(j);
983 if (rcomp.compare(s1, s2) == 0) {
984 Resource t = list2.get(js);
994 private void compareStatements(List<Statement> ss1, List<Statement> ss2, Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {
995 sortStatement(ss1, ss2);
1001 if (i1 >= ss1.size()) {
1002 if (i2 >= ss2.size()) {
1005 while (i2 < ss2.size()) {
1006 if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i2)));
1008 addAddition(ss2.get(i2));
1013 } else if (i2 >= ss2.size()) {
1014 while (i1 < ss1.size()) {
1015 if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i1)));
1016 addDeletion(ss1.get(i1));
1021 int same1 = sameRel(ss1, i1);
1022 int same2 = sameRel(ss2, i2);
1023 int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());
1025 compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight);
1029 for (int i = 0; i < same1; i++) {
1030 if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i+i1)));
1031 addDeletion(ss1.get(i+i1));
1035 for (int i = 0; i < same2; i++) {
1036 if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i+i2)));
1037 addAddition(ss2.get(i+i2));
1047 private int sameRel(List<Statement> statements, int off) {
1048 if (statements.size() <= off)
1051 long id = statements.get(off).getPredicate().getResourceId();
1052 for (int i = off+1; i <statements.size(); i++) {
1053 if (statements.get(i).getPredicate().getResourceId() == id)
1062 private int compareObject(Resource o1, Resource o2) throws DatabaseException {
1065 if (comparableResources.contains(o1, o2))
1067 if (comparableResources.containsLeft(o1))
1068 return Integer.MAX_VALUE;
1069 if (comparableResources.containsRight(o2))
1070 return Integer.MAX_VALUE;
1071 if (nonMatchedLeft.contains(o1))
1072 return Integer.MAX_VALUE;
1073 if (nonMatchedRight.contains(o2))
1074 return Integer.MAX_VALUE;
1075 return comparator.compare(g, o1, o2);
1078 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 {
1079 boolean[] used1 = new boolean[len1];
1080 for (int i = 0; i < used1.length; i++) {
1084 boolean[] used2 = new boolean[len2];
1085 for (int i = 0; i < used2.length; i++) {
1089 // left, right, difference
1090 List<List<Integer>> differences = new ArrayList<List<Integer>>();
1091 for (int i1 = off1; i1 < off1 + len1; i1++) {
1092 Statement s1 = ss1.get(i1);
1093 List<Integer> diff = new ArrayList<Integer>();
1094 for (int i2 = off2; i2 < off2 + len2; i2++) {
1095 Statement s2 = ss2.get(i2);
1096 int d = compareObject(s1.getObject(), s2.getObject());
1098 for (Resource t : strong) {
1099 if (s1.getPredicate().equals(t) || g.isSubrelationOf(s1.getPredicate(), t)) {
1107 differences.add(diff);
1110 MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();
1111 for (int i = 0; i < differences.size(); i++) {
1112 List<Integer> list = differences.get(i);
1113 for (int j = 0; j < list.size(); j++) {
1114 priorities.add(list.get(j), i);
1118 Integer[] pris = priorities.getKeys(new Integer[]{});
1120 boolean matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, false);
1122 matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, objectsLeft == null);
1124 for (Integer pri : pris) {
1125 if (pri != 0 && !matchFail && unreliableLeft == null)
1127 Set<Statement> s1s = new HashSet<Statement>();
1128 Set<Statement> s2s = new HashSet<Statement>();
1129 Set<Integer> s1i = new HashSet<Integer>();
1130 Set<Integer> s2i = new HashSet<Integer>();
1131 List<Integer> i1s = priorities.getValues(pri);
1132 for (Integer i1 : i1s) {
1135 List<Integer> i2diff = differences.get(i1);
1136 for (int i2 = 0; i2 < i2diff.size(); i2++) {
1137 if (i2diff.get(i2) == pri) {
1140 Statement s1 = ss1.get(i1+off1);
1141 Statement s2 = ss2.get(i2+off2);
1149 if (unreliableLeft != null) {
1150 unreliableLeft.addAll(s1s);
1151 unreliableRight.addAll(s2s);
1153 for (Integer i : s1i)
1155 for (Integer i : s2i)
1159 for (int i1 = off1; i1 < off1 + len1; i1++) {
1160 if (!used1[i1-off1]) {
1161 if (DEBUG) System.out.println("Compare Object deletion " + printStatement(g,ss1.get(i1)));
1162 addDeletion(ss1.get(i1));
1165 for (int i2 = off2; i2 < off2 + len2; i2++) {
1166 if (!used2[i2-off2]) {
1167 if (DEBUG) System.out.println("Compare Object addition " + printStatement(g,ss2.get(i2)));
1168 addAddition(ss2.get(i2));
1173 private boolean priorityMatching(List<Statement> ss1, int off1, int len1, List<Statement> ss2, int off2, int len2, Integer[] pris, List<List<Integer>> differences, MapList<Integer, Integer> priorities, boolean used1[],boolean used2[], Collection<Resource> objectsLeft, Collection<Resource> objectsRight, boolean force) throws DatabaseException {
1174 boolean matchFail = false;
1175 for (Integer pri : pris) {
1176 if (pri == Integer.MAX_VALUE) {
1178 } else if (pri == 0) {
1181 List<Integer> i1s = priorities.getValues(pri);
1183 for (Integer i1 : i1s) {
1186 List<Integer> i2diff = differences.get(i1);
1187 List<Integer> matches = new ArrayList<Integer>();
1188 for (int i2 = 0; i2 < i2diff.size(); i2++) {
1189 if (i2diff.get(i2) == pri) {
1195 if (matches.size() == 1 || (force && matches.size() > 1)) {
1196 int i2 = matches.get(0);
1199 Statement s1 = ss1.get(i1+off1);
1200 Statement s2 = ss2.get(i2+off2);
1202 if (objectsLeft != null) {
1203 objectsLeft.add(s1.getObject());
1204 objectsRight.add(s2.getObject());
1206 addComparable(s1, s2);
1207 } else if (matches.size() > 1) {
1220 * compares properties, assumes functional relations
1223 * @throws ServiceException
1224 * @throws DoesNotContainValueException
1225 * @throws ValidationException
1227 private void compareProps(Resource r1, Resource r2) throws DatabaseException {
1228 if (DEBUG) System.out.println("compareProps " + r1 + " " + NameUtils.getSafeName(g, r1) + " " + r2 + " " + NameUtils.getSafeName(g, r2));
1229 List<Statement> ss1 = new ArrayList<Statement>();
1230 List<Statement> ss2 = new ArrayList<Statement>();
1231 ss1.addAll(g.getStatements(r1, b.HasProperty));
1232 ss2.addAll(g.getStatements(r2, b.HasProperty));
1233 ss1 = filterNonTested(ss1);
1234 ss2 = filterNonTested(ss2);
1235 sortStatement(ss1, ss2);
1236 // getStatements(r1, b.HasProperty) returns both instance and asserted statements for the same property relation.
1237 ss1 = filterAssertedDuplicates(r1, ss1);
1238 ss2 = filterAssertedDuplicates(r2, ss2);
1244 if (i1 >= ss1.size()) {
1245 if (i2 >= ss2.size())
1248 while (i2 < ss2.size()) {
1249 Statement s = ss2.get(i2);
1250 if (DEBUG) System.out.println("Compare Prop diff2 " + printStatement(g,s));
1251 if (!s.isAsserted(r2))
1257 } else if (i2 >= ss2.size()) {
1258 while (i1 < ss1.size()) {
1259 Statement s = ss1.get(i1);
1260 if (DEBUG) System.out.println("Compare Prop diff1 " + printStatement(g,s));
1261 if (!s.isAsserted(r1))
1267 Statement s1 = ss1.get(i1);
1268 Statement s2 = ss2.get(i2);
1269 if (s1.isAsserted(r1) && s2.isAsserted(r2)) {
1274 int c = scomp.compare(s1, s2);
1277 boolean b1 = g.hasValue(s1.getObject());
1278 boolean b2 = g.hasValue(s2.getObject());
1279 boolean a1 = s1.isAsserted(r1);
1280 boolean a2 = s2.isAsserted(r2);
1284 boolean eq = compareValue(g,b,s1.getObject(), s2.getObject());
1286 addModification(r1,s1,r2,s2);
1288 addComparable(s1, s2);
1291 // Non literal properties.
1292 int comp = comparator.compare(g, s1.getObject(), s2.getObject());
1293 if (comp == ResourceComparator.NO_MATCH) {
1294 addModification(r1,s1,r2,s2);
1295 } else if (comp != ResourceComparator.EXACT_MATCH) {
1296 if (!s1.getObject().equals(s1.getSubject()) && !s2.getObject().equals(s2.getSubject())) {
1298 // compare props matches objects, so we can call that only for non asserted statements
1299 compareProps(s1.getObject(), s2.getObject());
1300 } else if (a1 && a2) {
1301 // TODO : compare asserted statements?
1303 addModification(r1,s1,r2,s2);
1306 addModification(r1,s1,r2,s2);
1309 // Exact match, nothing to do.
1313 addModification(r1,s1,r2,s2);
1320 if (DEBUG) System.out.println("Compare Prop diff1s " + printStatement(g,s1));
1321 // Use modification, since deletions do not support asserted statements
1322 addModification(r1,s1,r2,null);
1329 if (DEBUG) System.out.println("Compare Prop diff2s " + printStatement(g,s2));
1330 // Use modification, since additions do not support asserted statements
1331 addModification(r1,null,r2,s2);
1345 public static boolean compareValue(ReadGraph g, Layer0 b, Resource r1, Resource r2) throws DatabaseException {
1346 Resource t1 = g.getSingleType(r1);
1347 Resource t2 = g.getSingleType(r2);
1350 if (t1.equals(b.Integer)) {
1351 int v1 = g.getValue(r1, Bindings.INTEGER);
1352 int v2 = g.getValue(r2, Bindings.INTEGER);
1354 } else if (t1.equals(b.Float)) {
1355 float v1 = g.getValue(r1, Bindings.FLOAT);
1356 float v2 = g.getValue(r2, Bindings.FLOAT);
1358 } else if (t1.equals(b.Double)) {
1359 double v1 = g.getValue(r1, Bindings.DOUBLE);
1360 double v2 = g.getValue(r2, Bindings.DOUBLE);
1362 } else if (t1.equals(b.String)) {
1363 String v1 = g.getValue(r1, Bindings.STRING);
1364 String v2 = g.getValue(r2, Bindings.STRING);
1365 return v1.equals(v2);
1366 } else if (t1.equals(b.Boolean)) {
1367 boolean v1 = g.getValue(r1, Bindings.BOOLEAN);
1368 boolean v2 = g.getValue(r2, Bindings.BOOLEAN);
1370 } else if (t1.equals(b.Byte)) {
1371 byte v1 = g.getValue(r1, Bindings.BYTE);
1372 byte v2 = g.getValue(r2, Bindings.BYTE);
1374 } else if (t1.equals(b.Long)) {
1375 long v1 = g.getValue(r1, Bindings.LONG);
1376 long v2 = g.getValue(r2, Bindings.LONG);
1378 } else if (t1.equals(b.IntegerArray)) {
1379 int[] v1 = g.getValue(r1, Bindings.INT_ARRAY);
1380 int[] v2 = g.getValue(r2, Bindings.INT_ARRAY);
1381 return Arrays.equals(v1,v2);
1382 } else if (t1.equals(b.FloatArray)) {
1383 float[] v1 = g.getValue(r1, Bindings.FLOAT_ARRAY);
1384 float[] v2 = g.getValue(r2, Bindings.FLOAT_ARRAY);
1385 return Arrays.equals(v1,v2);
1386 } else if (t1.equals(b.DoubleArray)) {
1387 double[] v1 = g.getValue(r1, Bindings.DOUBLE_ARRAY);
1388 double[] v2 = g.getValue(r2, Bindings.DOUBLE_ARRAY);
1389 return Arrays.equals(v1,v2);
1390 } else if (t1.equals(b.StringArray)) {
1391 String[] v1 = g.getValue(r1, Bindings.STRING_ARRAY);
1392 String[] v2 = g.getValue(r2, Bindings.STRING_ARRAY);
1393 return Arrays.equals(v1,v2);
1394 } else if (t1.equals(b.BooleanArray)) {
1395 boolean[] v1 = g.getValue(r1, Bindings.BOOLEAN_ARRAY);
1396 boolean[] v2 = g.getValue(r2, Bindings.BOOLEAN_ARRAY);
1397 return Arrays.equals(v1,v2);
1398 } else if (t1.equals(b.ByteArray)) {
1399 byte[] v1 = g.getValue(r1, Bindings.BYTE_ARRAY);
1400 byte[] v2 = g.getValue(r2, Bindings.BYTE_ARRAY);
1401 return Arrays.equals(v1,v2);
1402 } else if (t1.equals(b.LongArray)) {
1403 long[] v1 = g.getValue(r1, Bindings.LONG_ARRAY);
1404 long[] v2 = g.getValue(r2, Bindings.LONG_ARRAY);
1405 return Arrays.equals(v1,v2);
1407 Object v1 = g.getValue(r1);
1408 Object v2 = g.getValue(r2);
1409 return compareValue(v1, v2);
1414 public static boolean compareValue(Object v1, Object v2) {
1415 if (v1 instanceof Object[] && v2 instanceof Object[])
1416 return Arrays.deepEquals((Object[])v1, (Object[])v2);
1417 else if (v1 instanceof int[] && v2 instanceof int[])
1418 return Arrays.equals((int[])v1, (int[])v2);
1419 else if (v1 instanceof float[] && v2 instanceof float[])
1420 return Arrays.equals((float[])v1, (float[])v2);
1421 else if (v1 instanceof double[] && v2 instanceof double[])
1422 return Arrays.equals((double[])v1, (double[])v2);
1423 else if (v1 instanceof long[] && v2 instanceof long[])
1424 return Arrays.equals((long[])v1, (long[])v2);
1425 else if (v1 instanceof byte[] && v2 instanceof byte[])
1426 return Arrays.equals((byte[])v1, (byte[])v2);
1427 else if (v1 instanceof boolean[] && v2 instanceof boolean[])
1428 return Arrays.equals((boolean[])v1, (boolean[])v2);
1430 return v1.equals(v2);
1434 public class PredicateComparator implements Comparator<Statement> {
1436 public int compare(Statement o1, Statement o2) {
1437 if (comparableResources.contains(o1.getPredicate(), o2.getPredicate()))
1439 if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
1441 if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
1447 public class SubjectComparator implements Comparator<Statement> {
1449 public int compare(Statement o1, Statement o2) {
1450 if (comparableResources.contains(o1.getSubject(), o2.getSubject()))
1452 if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
1454 if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
1460 public class ObjectComparator implements Comparator<Statement> {
1462 public int compare(Statement o1, Statement o2) {
1463 if (comparableResources.contains(o1.getObject(), o2.getObject()))
1465 if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
1467 if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
1473 public static class FullStatementComparator implements Comparator<Statement> {
1475 public int compare(Statement o1, Statement o2) {
1476 if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
1478 if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
1480 if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
1482 if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
1484 if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
1486 if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
1492 public class ResComparator implements Comparator<Resource> {
1494 public int compare(Resource o1, Resource o2) {
1495 if (comparableResources.contains(o1, o2))
1497 if (o1.getResourceId() < o2.getResourceId())
1499 if (o1.getResourceId() > o2.getResourceId())