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.List;
\r
20 import java.util.Stack;
\r
22 import org.simantics.db.Builtins;
\r
23 import org.simantics.db.ReadGraph;
\r
24 import org.simantics.db.Resource;
\r
25 import org.simantics.db.Statement;
\r
26 import org.simantics.db.exception.DoesNotContainValueException;
\r
27 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
\r
28 import org.simantics.db.exception.ServiceException;
\r
29 import org.simantics.db.exception.ValidationException;
\r
30 import org.simantics.layer0.utils.direct.GraphUtils;
\r
31 import org.simantics.utils.datastructures.BijectionMap;
\r
32 import org.simantics.utils.datastructures.MapList;
\r
35 * Compares two subgraphs and reports differences.
\r
37 * Assumes that subgraphs (defined using traverse relations) are not cyclic.
\r
39 * Assumes that properties can be used to identify objects, if relation type is not enough.
\r
43 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
46 public class GraphComparator {
\r
49 private List<Resource> traversed = new ArrayList<Resource>(); // list of relations that are traversed (and tested)
\r
50 private List<Resource> tested = new ArrayList<Resource>(); // list of relations that are tested, but not traversed
\r
53 private List<Statement> changes1 = new ArrayList<Statement>();
\r
54 private List<Statement> changes2 = new ArrayList<Statement>();
\r
55 private BijectionMap<Statement, Statement> comparable = new BijectionMap<Statement, Statement>();
\r
58 // runtime attributes
\r
60 private ReadGraph g;
\r
63 ArrayList<Resource> rs1 = new ArrayList<Resource>();
\r
64 ArrayList<Resource> rs2 = new ArrayList<Resource>();
\r
65 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
66 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
67 Comparator<Statement> scomp = new StatementComparator();
\r
68 Comparator<Resource> rcomp = new ResourceComparator();
\r
71 public void addTraversed(Resource rel) {
\r
75 public void addTraversed(Collection<Resource> rels) {
\r
76 traversed.addAll(rels);
\r
79 public void addTested(Resource rel) {
\r
83 public void addTested(Collection<Resource> rels) {
\r
84 tested.addAll(rels);
\r
87 public void clearRels() {
\r
92 public void test(ReadGraph g, Resource r1, Resource r2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
94 this.b = g.getBuiltins();
\r
99 Stack<Resource> stack1 = new Stack<Resource>();
\r
100 Stack<Resource> stack2 = new Stack<Resource>();
\r
104 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
105 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
107 while (!stack1.isEmpty()) {
\r
111 System.out.println("test " + GraphUtils.getReadableName(g, r1) + " " + GraphUtils.getReadableName(g, r2));
\r
112 compareProps(r1, r2);
\r
114 for (Resource rel : tested) {
\r
115 filterTraversed(g.getStatements(r1, rel), ss1);
\r
116 filterTraversed(g.getStatements(r2, rel), ss2);
\r
118 compareStatement(ss1, ss2, null, null);
\r
123 for (Resource rel : traversed) {
\r
124 ss1.addAll(g.getStatements(r1, rel));
\r
125 ss2.addAll(g.getStatements(r2, rel));
\r
126 compareStatement(ss1, ss2, stack1, stack2);
\r
133 public Collection<Statement> getChanges1() {
\r
137 public Collection<Statement> getChanges2() {
\r
141 public BijectionMap<Statement, Statement> getComparable() {
\r
145 private void filterTraversed(Collection<Statement> in, Collection<Statement> out) throws ServiceException {
\r
146 for (Statement s : in) {
\r
147 boolean usable = true;
\r
148 for (Resource r : traversed) {
\r
149 if (g.isSubrelationOf(s.getPredicate(),r)) {
\r
161 private void compareStatement(List<Statement> ss1, List<Statement> ss2, Stack<Resource> stack1, Stack<Resource> stack2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
162 Collections.sort(ss1, scomp);
\r
163 Collections.sort(ss2, scomp);
\r
169 if (i1 >= ss1.size()) {
\r
170 if (i2 >= ss2.size()) {
\r
173 while (i2 < ss2.size()) {
\r
174 changes2.add(ss2.get(i2));
\r
179 } else if (i2 >= ss2.size()) {
\r
180 while (i1 < ss1.size()) {
\r
181 changes1.add(ss1.get(i1));
\r
186 int same1 = sameRel(ss1, i1);
\r
187 int same2 = sameRel(ss2, i2);
\r
188 int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());
\r
190 compareObject(ss1, i1, same1, ss2, i2, same2,stack1,stack2);
\r
193 } else if (c < 0) {
\r
194 for (int i = 0; i < same1; i++) {
\r
195 changes1.add(ss1.get(i+i1));
\r
199 for (int i = 0; i < same2; i++) {
\r
200 changes2.add(ss2.get(i+i2));
\r
208 private int sameRel(List<Statement> statements, int off) {
\r
209 if (statements.size() <= off)
\r
212 long id = statements.get(off).getPredicate().getResourceId();
\r
213 for (int i = off+1; i <statements.size(); i++) {
\r
214 if (statements.get(i).getPredicate().getResourceId() == id)
\r
224 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
225 boolean[] used1 = new boolean[len1];
\r
226 for (int i = 0; i < used1.length; i++) {
\r
230 boolean[] used2 = new boolean[len2];
\r
231 for (int i = 0; i < used2.length; i++) {
\r
235 // left, right, difference
\r
236 List<List<Integer>> differences = new ArrayList<List<Integer>>();
\r
237 for (int i1 = off1; i1 < off1 + len1; i1++) {
\r
238 Statement s1 = ss1.get(i1);
\r
239 //Map<Integer,Integer> differences = new HashMap<Integer, Integer>();
\r
240 List<Integer> diff = new ArrayList<Integer>();
\r
241 for (int i2 = off2; i2 < off2 + len2; i2++) {
\r
242 Statement s2 = ss2.get(i2);
\r
243 if (!compareType(s1.getObject(), s2.getObject())) {
\r
244 diff.add(Integer.MAX_VALUE);
\r
247 diff.add(propsDiffCount(s1.getObject(), s2.getObject()));
\r
249 differences.add(diff);
\r
251 // difference, left
\r
252 MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();
\r
253 for (int i = 0; i < differences.size(); i++) {
\r
254 List<Integer> list = differences.get(i);
\r
255 for (int j = 0; j < list.size(); j++) {
\r
256 priorities.add(list.get(j), i);
\r
260 Integer[] pris = priorities.getKeys(new Integer[]{});
\r
263 for (Integer pri : pris) {
\r
264 if (pri == Integer.MAX_VALUE)
\r
266 List<Integer> i1s = priorities.getValues(pri);
\r
267 for (Integer i1 : i1s) {
\r
270 List<Integer> i2diff = differences.get(i1);
\r
271 for (int i2 = 0; i2 < i2diff.size(); i2++) {
\r
272 if (i2diff.get(i2) == pri) {
\r
277 if (stack1 != null) {
\r
278 stack1.add(ss1.get(i1+off1).getObject());
\r
279 stack2.add(ss2.get(i2+off2).getObject());
\r
281 // TODO : how should we report non traversed differences
\r
282 // using compareProps assumes that referenced objects are the same (references are not different)
\r
283 // using propsDiffCount assumes that objects are different, and cannot report changes in referred object.
\r
285 //compareProps(ss1.get(i1+off1).getObject(), ss2.get(i2+off2).getObject());
\r
286 int diff = propsDiffCount(ss1.get(i1+off1).getObject(), ss2.get(i2+off2).getObject());
\r
288 changes1.add(ss1.get(i1+off1));
\r
289 changes2.add(ss2.get(i2+off2));
\r
296 for (int i1 = off1; i1 < off1 + len1; i1++) {
\r
297 if (!used1[i1-off1])
\r
298 changes1.add(ss1.get(i1));
\r
300 for (int i2 = off2; i2 < off2 + len2; i2++) {
\r
301 if (!used2[i2-off2])
\r
302 changes2.add(ss2.get(i2));
\r
306 private boolean compareType(Resource r1, Resource r2) throws ServiceException, ManyObjectsForFunctionalRelationException {
\r
307 rs1.addAll(g.getObjects(r1, b.InstanceOf));
\r
308 rs2.addAll(g.getObjects(r2, b.InstanceOf));
\r
309 if (rs1.size() != rs2.size()) {
\r
314 Collections.sort(rs1, rcomp);
\r
315 Collections.sort(rs2, rcomp);
\r
316 for (int i = 0; i < rs1.size(); i++) {
\r
317 int c = rcomp.compare(rs1.get(i), rs2.get(i));
\r
332 * compares properties, assumes functional relations
\r
335 * @throws ManyObjectsForFunctionalRelationException
\r
336 * @throws ServiceException
\r
337 * @throws DoesNotContainValueException
\r
339 private void compareProps(Resource r1, Resource r2) throws ManyObjectsForFunctionalRelationException, ServiceException, DoesNotContainValueException {
\r
340 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
341 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
342 ss1.addAll(g.getStatements(r1, b.HasProperty));
\r
343 ss2.addAll(g.getStatements(r2, b.HasProperty));
\r
344 Collections.sort(ss1, scomp);
\r
345 Collections.sort(ss2, scomp);
\r
351 if (i1 >= ss1.size()) {
\r
352 if (i2 >= ss2.size())
\r
355 while (i2 < ss2.size()) {
\r
356 changes2.add(ss2.get(i2));
\r
361 } else if (i2 >= ss2.size()) {
\r
362 while (i1 < ss1.size()) {
\r
363 changes1.add(ss1.get(i1));
\r
368 Statement s1 = ss1.get(i1);
\r
369 Statement s2 = ss2.get(i2);
\r
370 int c = scomp.compare(s1, s2);
\r
373 boolean b1 = g.hasValue(s1.getObject());
\r
374 boolean b2 = g.hasValue(s2.getObject());
\r
377 Object v1 = g.getValue(s1.getObject());
\r
378 Object v2 = g.getValue(s2.getObject());
\r
379 boolean eq = false;
\r
380 if (v1 instanceof Object[] && v2 instanceof Object[])
\r
381 eq = Arrays.deepEquals((Object[])v1, (Object[])v2);
\r
383 eq = v1.equals(v2);
\r
387 comparable.map(s1, s2);
\r
390 compareProps(s1.getObject(), s2.getObject());
\r
395 comparable.map(s1, s2);
\r
422 private int propsDiffCount(Resource r1, Resource r2) throws ServiceException, DoesNotContainValueException, ValidationException {
\r
423 ArrayList<Statement> ss1 = new ArrayList<Statement>();
\r
424 ArrayList<Statement> ss2 = new ArrayList<Statement>();
\r
425 ss1.addAll(g.getStatements(r1, b.HasProperty));
\r
426 ss2.addAll(g.getStatements(r2, b.HasProperty));
\r
427 //System.out.println("Props count " + GraphUtils.getReadableName(g, r1) + " " + GraphUtils.getReadableName(g, r2));
\r
428 Collections.sort(ss1, scomp);
\r
429 Collections.sort(ss2, scomp);
\r
437 if (i1 >= ss1.size()) {
\r
438 if (i2 >= ss2.size())
\r
441 while (i2 < ss2.size()) {
\r
447 } else if (i2 >= ss2.size()) {
\r
448 while (i1 < ss1.size()) {
\r
454 Statement s1 = ss1.get(i1);
\r
455 Statement s2 = ss2.get(i2);
\r
456 int c = scomp.compare(s1, s2);
\r
459 boolean b1 = g.hasValue(s1.getObject());
\r
460 boolean b2 = g.hasValue(s2.getObject());
\r
463 Object v1 = g.getValue(s1.getObject());
\r
464 Object v2 = g.getValue(s2.getObject());
\r
465 boolean eq = false;
\r
466 if (v1 instanceof Object[] && v2 instanceof Object[])
\r
467 eq = Arrays.deepEquals((Object[])v1, (Object[])v2);
\r
469 eq = v1.equals(v2);
\r
473 //System.out.println("Prop count values " + v1 + " " + v2);
\r
475 count += propsDiffCount(s1.getObject(), s2.getObject());
\r
478 //System.out.println("Props count structural vs literal");
\r
508 public class StatementComparator implements Comparator<Statement> {
\r
510 public int compare(Statement o1, Statement o2) {
\r
511 if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
\r
513 if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
\r
521 public class ResourceComparator implements Comparator<Resource> {
\r
523 public int compare(Resource o1, Resource o2) {
\r
524 if (o1.getResourceId() < o2.getResourceId())
\r
526 if (o2.getResourceId() > o2.getResourceId())
\r