1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * Foster Wheeler Energia Oy - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.interop.test;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Arrays;
\r
16 import java.util.Collection;
\r
17 import java.util.Collections;
\r
18 import java.util.Comparator;
\r
19 import java.util.HashSet;
\r
20 import java.util.List;
\r
21 import java.util.Set;
\r
22 import java.util.Stack;
\r
24 import org.simantics.db.ReadGraph;
\r
25 import org.simantics.db.Resource;
\r
26 import org.simantics.db.Statement;
\r
27 import org.simantics.db.common.utils.NameUtils;
\r
28 import org.simantics.db.exception.DoesNotContainValueException;
\r
29 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
\r
30 import org.simantics.db.exception.ServiceException;
\r
31 import org.simantics.db.exception.ValidationException;
\r
32 import org.simantics.layer0.Layer0;
\r
33 import org.simantics.utils.datastructures.BijectionMap;
\r
34 import org.simantics.utils.datastructures.MapList;
\r
35 import org.simantics.utils.datastructures.Pair;
\r
38 * Compares two subgraphs and reports differences.
\r
40 * Assumes that subgraphs (defined using traverse relations) are not cyclic.
\r
42 * Assumes that properties can be used to identify objects, if relation type is not enough.
\r
46 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
49 public class GraphComparator {
\r
52 private List<Resource> traversed = new ArrayList<Resource>(); // list of relations that are traversed (and tested)
\r
53 private List<Resource> tested = new ArrayList<Resource>(); // list of relations that are tested, but not traversed
\r
54 private List<Resource> nonTraversed = new ArrayList<Resource>(); // list of relations that are not traversed
\r
55 private List<Resource> nonTested = new ArrayList<Resource>(); // list of relations that are not tested
\r
57 private List<Statement> changes1 = new ArrayList<Statement>();
\r
58 private List<Statement> changes2 = new ArrayList<Statement>();
\r
59 private List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
\r
60 private Set<Statement> changes1Set = new HashSet<Statement>();
\r
61 private Set<Statement> changes2Set = new HashSet<Statement>();
\r
62 private Set<Pair<Statement,Statement>> modificationsSet = new HashSet<Pair<Statement,Statement>>();
\r
64 private BijectionMap<Statement, Statement> comparableStatements = new BijectionMap<Statement, Statement>();
\r
65 private BijectionMap<Resource, Resource> comparableResources = new BijectionMap<Resource, Resource>();
\r
68 // runtime attributes
\r
70 private ReadGraph g;
\r
73 ArrayList<Resource> rs1 = new ArrayList<Resource>();
\r
74 ArrayList<Resource> rs2 = new ArrayList<Resource>();
\r
75 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
76 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
77 Comparator<Statement> scomp = new PredicateComparator();
\r
78 Comparator<Resource> rcomp = new ResourceComparator();
\r
81 public void addTraversed(Resource rel) {
\r
85 public void addTraversed(Collection<Resource> rels) {
\r
86 traversed.addAll(rels);
\r
89 public void addNonTraversed(Resource rel) {
\r
90 nonTraversed.add(rel);
\r
93 public void addNonTraversed(Collection<Resource> rels) {
\r
94 nonTraversed.addAll(rels);
\r
97 public void addTested(Resource rel) {
\r
101 public void addTested(Collection<Resource> rels) {
\r
102 tested.addAll(rels);
\r
105 public void addNonTested(Resource rel) {
\r
106 nonTested.add(rel);
\r
109 public void addNonTested(Collection<Resource> rels) {
\r
110 nonTested.addAll(rels);
\r
113 public void clearRels() {
\r
118 public void test(ReadGraph g, Resource r1, Resource r2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
120 this.b = Layer0.getInstance(g);
\r
122 changes1Set.clear();
\r
123 changes2Set.clear();
\r
124 modificationsSet.clear();
\r
127 modifications.clear();
\r
128 comparableResources.clear();
\r
130 Stack<Resource> stack1 = new Stack<Resource>();
\r
131 Stack<Resource> stack2 = new Stack<Resource>();
\r
136 List<Statement> ss1 = new ArrayList<Statement>();
\r
137 List<Statement> ss2 = new ArrayList<Statement>();
\r
139 while (!stack1.isEmpty()) {
\r
143 if (comparableResources.contains(r1, r2)) {
\r
144 System.out.println("already tested " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));
\r
147 comparableResources.map(r1, r2);
\r
149 System.out.println("test " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));
\r
150 compareProps(r1, r2);
\r
152 for (Resource rel : tested) {
\r
153 ss1.addAll(g.getStatements(r1, rel));
\r
154 ss2.addAll(g.getStatements(r2, rel));
\r
155 ss1 = filterAsserted(r1, ss1);
\r
156 ss2 = filterAsserted(r2, ss2);
\r
157 ss1 = filterTraversed(ss1);
\r
158 ss2 = filterTraversed(ss2);
\r
159 ss1 = filterNonTested(ss1);
\r
160 ss2 = filterNonTested(ss2);
\r
162 compareStatement(ss1, ss2, null, null);
\r
167 for (Resource rel : traversed) {
\r
168 ss1.addAll(g.getStatements(r1, rel));
\r
169 ss2.addAll(g.getStatements(r2, rel));
\r
170 ss1 = filterAsserted(r1, ss1);
\r
171 ss2 = filterAsserted(r2, ss2);
\r
172 compareStatement(ss1, ss2, stack1, stack2);
\r
181 public BijectionMap<Statement, Statement> getComparableStatements() {
\r
182 return comparableStatements;
\r
185 public GraphChanges getChanges() {
\r
186 // List<Statement> deletions = new ArrayList<Statement>();
\r
187 // List<Statement> additions = new ArrayList<Statement>();
\r
188 // deletions.addAll(changes1);
\r
189 // additions.addAll(changes2);
\r
190 // Collections.sort(deletions, new FullStatementComparator());
\r
191 // Collections.sort(additions, new FullStatementComparator());
\r
192 // return new GraphChanges(deletions, additions, modifications);
\r
193 return new GraphChanges(changes1,changes2,modifications);
\r
196 private List<Statement> filterAsserted(Resource r, Collection<Statement> in) throws ServiceException {
\r
197 List<Statement> out = new ArrayList<Statement>();
\r
198 for (Statement s : in) {
\r
199 if (!s.isAsserted(r))
\r
206 private String printStatement(ReadGraph graph, Statement s) throws ValidationException, ServiceException {
\r
207 return NameUtils.getSafeName(graph, s.getSubject()) + " " + NameUtils.getSafeName(graph, s.getPredicate()) + " " + NameUtils.getSafeName(graph, s.getObject());
\r
210 private List<Statement> filterTraversed(List<Statement> in) throws ServiceException {
\r
211 return filter(traversed, in);
\r
214 private List<Statement> filterNonTested(List<Statement> in) throws ServiceException {
\r
215 return filter(nonTested, in);
\r
218 private List<Statement> filter(Collection<Resource> toFilter, List<Statement> in) throws ServiceException {
\r
219 if (toFilter.size() == 0)
\r
221 List<Statement> out = new ArrayList<Statement>();
\r
222 for (Statement s : in) {
\r
223 boolean usable = true;
\r
224 for (Resource r : toFilter) {
\r
225 if (g.isSubrelationOf(s.getPredicate(),r)) {
\r
239 private void addDeletion(Statement s) {
\r
240 if (!changes1Set.contains(s)) {
\r
241 changes1Set.add(s);
\r
246 private void addAddition(Statement s) {
\r
247 if (!changes2Set.contains(s)) {
\r
248 changes2Set.add(s);
\r
253 private void addModification(Statement s1, Statement s2) {
\r
254 Pair<Statement, Statement> mod = new Pair<Statement, Statement>(s1,s2);
\r
255 if (!modificationsSet.contains(mod)) {
\r
256 modificationsSet.add(mod);
\r
257 modifications.add(mod);
\r
261 private void compareStatement(List<Statement> ss1, List<Statement> ss2, Stack<Resource> stack1, Stack<Resource> stack2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
262 Collections.sort(ss1, scomp);
\r
263 Collections.sort(ss2, scomp);
\r
269 if (i1 >= ss1.size()) {
\r
270 if (i2 >= ss2.size()) {
\r
273 while (i2 < ss2.size()) {
\r
274 System.out.println("Compare Statement diff2 " + printStatement(g,ss2.get(i2)));
\r
276 addAddition(ss2.get(i2));
\r
281 } else if (i2 >= ss2.size()) {
\r
282 while (i1 < ss1.size()) {
\r
283 System.out.println("Compare Statement diff1 " + printStatement(g,ss1.get(i1)));
\r
284 addDeletion(ss1.get(i1));
\r
289 int same1 = sameRel(ss1, i1);
\r
290 int same2 = sameRel(ss2, i2);
\r
291 int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());
\r
293 compareObject(ss1, i1, same1, ss2, i2, same2,stack1,stack2);
\r
296 } else if (c < 0) {
\r
297 for (int i = 0; i < same1; i++) {
\r
298 System.out.println("Compare Statement diff1 " + printStatement(g,ss1.get(i+i1)));
\r
299 addDeletion(ss1.get(i+i1));
\r
303 for (int i = 0; i < same2; i++) {
\r
304 System.out.println("Compare Statement diff2 " + printStatement(g,ss2.get(i+i2)));
\r
305 addAddition(ss2.get(i+i2));
\r
314 private int sameRel(List<Statement> statements, int off) {
\r
315 if (statements.size() <= off)
\r
318 long id = statements.get(off).getPredicate().getResourceId();
\r
319 for (int i = off+1; i <statements.size(); i++) {
\r
320 if (statements.get(i).getPredicate().getResourceId() == id)
\r
330 private void compareObject(List<Statement> ss1, int off1, int len1, List<Statement> ss2, int off2, int len2, Stack<Resource> stack1, Stack<Resource> stack2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
331 boolean[] used1 = new boolean[len1];
\r
332 for (int i = 0; i < used1.length; i++) {
\r
336 boolean[] used2 = new boolean[len2];
\r
337 for (int i = 0; i < used2.length; i++) {
\r
341 // left, right, difference
\r
342 List<List<Integer>> differences = new ArrayList<List<Integer>>();
\r
343 for (int i1 = off1; i1 < off1 + len1; i1++) {
\r
344 Statement s1 = ss1.get(i1);
\r
345 //Map<Integer,Integer> differences = new HashMap<Integer, Integer>();
\r
346 List<Integer> diff = new ArrayList<Integer>();
\r
347 for (int i2 = off2; i2 < off2 + len2; i2++) {
\r
348 Statement s2 = ss2.get(i2);
\r
349 if (!compareType(s1.getObject(), s2.getObject())) {
\r
350 diff.add(Integer.MAX_VALUE);
\r
353 if (comparableResources.contains(s1.getObject(), s2.getObject()))
\r
356 diff.add(propsDiffCount(s1.getObject(), s2.getObject()));
\r
358 differences.add(diff);
\r
360 // difference, left
\r
361 MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();
\r
362 for (int i = 0; i < differences.size(); i++) {
\r
363 List<Integer> list = differences.get(i);
\r
364 for (int j = 0; j < list.size(); j++) {
\r
365 priorities.add(list.get(j), i);
\r
369 Integer[] pris = priorities.getKeys(new Integer[]{});
\r
372 for (Integer pri : pris) {
\r
373 if (pri == Integer.MAX_VALUE)
\r
375 List<Integer> i1s = priorities.getValues(pri);
\r
376 for (Integer i1 : i1s) {
\r
379 List<Integer> i2diff = differences.get(i1);
\r
380 for (int i2 = 0; i2 < i2diff.size(); i2++) {
\r
381 if (i2diff.get(i2) == pri) {
\r
386 Statement s1 = ss1.get(i1+off1);
\r
387 Statement s2 = ss2.get(i2+off2);
\r
389 if (stack1 != null) {
\r
391 stack1.add(s1.getObject());
\r
392 stack2.add(s2.getObject());
\r
395 // TODO : how should we report non traversed differences
\r
396 // using compareProps assumes that referenced objects are the same (references are not different)
\r
397 // using propsDiffCount assumes that objects are different, and cannot report changes in referred object.
\r
400 // int diff = propsDiffCount(ss1.get(i1+off1).getObject(), ss2.get(i2+off2).getObject());
\r
401 // if (diff != 0) {
\r
402 // //changes1.add(ss1.get(i1+off1));
\r
403 // //changes2.add(ss2.get(i2+off2));
\r
404 // modifies.add(new Pair<Statement, Statement>(ss1.get(i1+off1), ss2.get(i2+off2)));
\r
407 comparableStatements.map(s1, s2);
\r
408 //comparableResources.map(s1.getObject(), s2.getObject());
\r
414 for (int i1 = off1; i1 < off1 + len1; i1++) {
\r
415 if (!used1[i1-off1]) {
\r
416 System.out.println("Compare Object diff1 " + printStatement(g,ss1.get(i1)));
\r
417 addDeletion(ss1.get(i1));
\r
420 for (int i2 = off2; i2 < off2 + len2; i2++) {
\r
421 if (!used2[i2-off2]) {
\r
422 System.out.println("Compare Object diff2 " + printStatement(g,ss2.get(i2)));
\r
423 addAddition(ss2.get(i2));
\r
428 private boolean compareType(Resource r1, Resource r2) throws ServiceException, ManyObjectsForFunctionalRelationException {
\r
429 rs1.addAll(g.getObjects(r1, b.InstanceOf));
\r
430 rs2.addAll(g.getObjects(r2, b.InstanceOf));
\r
431 if (rs1.size() != rs2.size()) {
\r
436 Collections.sort(rs1, rcomp);
\r
437 Collections.sort(rs2, rcomp);
\r
438 for (int i = 0; i < rs1.size(); i++) {
\r
439 int c = rcomp.compare(rs1.get(i), rs2.get(i));
\r
454 * compares properties, assumes functional relations
\r
457 * @throws ServiceException
\r
458 * @throws DoesNotContainValueException
\r
459 * @throws ValidationException
\r
461 private void compareProps(Resource r1, Resource r2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
462 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
463 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
464 ss1.addAll(g.getStatements(r1, b.HasProperty));
\r
465 ss2.addAll(g.getStatements(r2, b.HasProperty));
\r
466 Collections.sort(ss1, scomp);
\r
467 Collections.sort(ss2, scomp);
\r
473 if (i1 >= ss1.size()) {
\r
474 if (i2 >= ss2.size())
\r
477 while (i2 < ss2.size()) {
\r
478 System.out.println("Compare Prop diff2 " + printStatement(g,ss2.get(i2)));
\r
479 addAddition(ss2.get(i2));
\r
484 } else if (i2 >= ss2.size()) {
\r
485 while (i1 < ss1.size()) {
\r
486 System.out.println("Compare Prop diff1 " + printStatement(g,ss1.get(i1)));
\r
487 addDeletion(ss1.get(i1));
\r
492 Statement s1 = ss1.get(i1);
\r
493 Statement s2 = ss2.get(i2);
\r
494 int c = scomp.compare(s1, s2);
\r
497 boolean b1 = g.hasValue(s1.getObject());
\r
498 boolean b2 = g.hasValue(s2.getObject());
\r
501 Object v1 = g.getValue(s1.getObject());
\r
502 Object v2 = g.getValue(s2.getObject());
\r
503 boolean eq = false;
\r
504 if (v1 instanceof Object[] && v2 instanceof Object[])
\r
505 eq = Arrays.deepEquals((Object[])v1, (Object[])v2);
\r
506 else if (v1 instanceof int[] && v2 instanceof int[])
\r
507 eq = Arrays.equals((int[])v1, (int[])v2);
\r
508 else if (v1 instanceof float[] && v2 instanceof float[])
\r
509 eq = Arrays.equals((float[])v1, (float[])v2);
\r
510 else if (v1 instanceof double[] && v2 instanceof double[])
\r
511 eq = Arrays.equals((double[])v1, (double[])v2);
\r
512 else if (v1 instanceof long[] && v2 instanceof long[])
\r
513 eq = Arrays.equals((long[])v1, (long[])v2);
\r
514 else if (v1 instanceof byte[] && v2 instanceof byte[])
\r
515 eq = Arrays.equals((byte[])v1, (byte[])v2);
\r
516 else if (v1 instanceof boolean[] && v2 instanceof boolean[])
\r
517 eq = Arrays.equals((boolean[])v1, (boolean[])v2);
\r
519 eq = v1.equals(v2);
\r
521 //changes1.add(s1);
\r
522 //changes2.add(s2);
\r
523 addModification(s1, s2);
\r
524 comparableStatements.map(s1, s2);
\r
525 comparableResources.map(s1.getObject(),s2.getObject());
\r
528 compareProps(s1.getObject(), s2.getObject());
\r
531 //changes1.add(s1);
\r
532 //changes2.add(s2);
\r
533 addModification(s1, s2);
\r
534 comparableStatements.map(s1, s2);
\r
535 comparableResources.map(s1.getObject(),s2.getObject());
\r
542 System.out.println("Compare Prop diff1s " + printStatement(g,s1));
\r
549 System.out.println("Compare Prop diff2s " + printStatement(g,s2));
\r
564 private int propsDiffCount(Resource r1, Resource r2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
565 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
566 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
567 ss1.addAll(g.getStatements(r1, b.HasProperty));
\r
568 ss2.addAll(g.getStatements(r2, b.HasProperty));
\r
569 //System.out.println("Props count " + GraphUtils.getReadableName(g, r1) + " " + GraphUtils.getReadableName(g, r2));
\r
570 Collections.sort(ss1, scomp);
\r
571 Collections.sort(ss2, scomp);
\r
579 if (i1 >= ss1.size()) {
\r
580 if (i2 >= ss2.size())
\r
583 while (i2 < ss2.size()) {
\r
589 } else if (i2 >= ss2.size()) {
\r
590 while (i1 < ss1.size()) {
\r
596 Statement s1 = ss1.get(i1);
\r
597 Statement s2 = ss2.get(i2);
\r
598 int c = scomp.compare(s1, s2);
\r
601 boolean b1 = g.hasValue(s1.getObject());
\r
602 boolean b2 = g.hasValue(s2.getObject());
\r
605 Object v1 = g.getValue(s1.getObject());
\r
606 Object v2 = g.getValue(s2.getObject());
\r
607 boolean eq = false;
\r
608 if (v1 instanceof Object[] && v2 instanceof Object[])
\r
609 eq = Arrays.deepEquals((Object[])v1, (Object[])v2);
\r
610 else if (v1 instanceof int[] && v2 instanceof int[])
\r
611 eq = Arrays.equals((int[])v1, (int[])v2);
\r
612 else if (v1 instanceof float[] && v2 instanceof float[])
\r
613 eq = Arrays.equals((float[])v1, (float[])v2);
\r
614 else if (v1 instanceof double[] && v2 instanceof double[])
\r
615 eq = Arrays.equals((double[])v1, (double[])v2);
\r
616 else if (v1 instanceof long[] && v2 instanceof long[])
\r
617 eq = Arrays.equals((long[])v1, (long[])v2);
\r
618 else if (v1 instanceof byte[] && v2 instanceof byte[])
\r
619 eq = Arrays.equals((byte[])v1, (byte[])v2);
\r
620 else if (v1 instanceof boolean[] && v2 instanceof boolean[])
\r
621 eq = Arrays.equals((boolean[])v1, (boolean[])v2);
\r
623 eq = v1.equals(v2);
\r
627 //System.out.println("Prop count values " + v1 + " " + v2);
\r
629 count += propsDiffCount(s1.getObject(), s2.getObject());
\r
632 //System.out.println("Props count structural vs literal");
\r
661 public class PredicateComparator implements Comparator<Statement> {
\r
663 public int compare(Statement o1, Statement o2) {
\r
664 if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
\r
666 if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
\r
672 public class FullStatementComparator implements Comparator<Statement> {
\r
674 public int compare(Statement o1, Statement o2) {
\r
675 if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
\r
677 if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
\r
679 if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
\r
681 if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
\r
683 if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
\r
685 if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
\r
693 public class ResourceComparator implements Comparator<Resource> {
\r
695 public int compare(Resource o1, Resource o2) {
\r
696 if (o1.getResourceId() < o2.getResourceId())
\r
698 if (o1.getResourceId() > o2.getResourceId())
\r