]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop/src/org/simantics/interop/test/GraphComparator.java
Preventing random matches did not work.
[simantics/interop.git] / org.simantics.interop / src / org / simantics / interop / test / GraphComparator.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     Foster Wheeler Energia Oy - initial API and implementation
11  *     VTT Technical Research Centre of Finland - Improvements to comparison.
12  *     Semantum Oy - Improvements to comparison, various bug fixes. 
13  *******************************************************************************/
14 package org.simantics.interop.test;
15
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import java.util.Stack;
28
29 import org.simantics.databoard.Bindings;
30 import org.simantics.db.ReadGraph;
31 import org.simantics.db.Resource;
32 import org.simantics.db.Session;
33 import org.simantics.db.Statement;
34 import org.simantics.db.common.request.ReadRequest;
35 import org.simantics.db.common.utils.NameUtils;
36 import org.simantics.db.exception.DatabaseException;
37 import org.simantics.db.request.Read;
38 import org.simantics.interop.test.GraphChanges.Modification;
39 import org.simantics.layer0.Layer0;
40 import org.simantics.utils.datastructures.BijectionMap;
41 import org.simantics.utils.datastructures.MapList;
42 import org.simantics.utils.datastructures.Pair;
43
44 /**
45  * Compares two subgraphs and reports differences.
46  * 
47  * Assumes that subgraphs (defined using traverse relations) are not cyclic.
48  * 
49  * Assumes that properties can be used to identify objects, if relation type is not enough.
50  * 
51  * 
52  * 
53  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
54  *
55  */
56 public class GraphComparator {
57         
58         private static final boolean DEBUG = false;
59
60         private Resource r1;
61         private Resource r2;
62         private Set<Resource> strong = new HashSet<>();       // List of relations that identify object, if subject is already identified. 
63         private List<Resource> traversed = new ArrayList<>(); // list of relations that are traversed (and tested)
64         private List<Resource> tested = new ArrayList<>();    // list of relations that are tested, but not traversed
65         private List<Resource> nonTraversed = new ArrayList<>(); // list of relations that are not traversed
66         private List<Resource> nonTested = new ArrayList<>(); // list of relations that are not tested
67         
68         private List<Statement> changes1 = new ArrayList<>();
69         private List<Statement> changes2 = new ArrayList<>();
70         private List<Modification> modifications = new ArrayList<>();
71         private Set<Statement> changes1Set = new HashSet<>();
72         private Set<Statement> changes2Set = new HashSet<>();
73         private Set<Modification> modificationsSet = new HashSet<>();
74
75         private BijectionMap<Statement, Statement> comparableStatements = new BijectionMap<>();
76         private BijectionMap<Resource, Resource> comparableResources = new BijectionMap<>();
77         
78         private Set<Resource> processedResources = new HashSet<Resource>();
79         
80         private ResourceComparator comparator;
81         
82         private Comparator<Statement> scomp = new PredicateComparator();
83         private Comparator<Resource> rcomp = new ResComparator();
84         
85         private Set<Resource> nonMatchedLeft = new HashSet<Resource>();
86         private Set<Resource> nonMatchedRight = new HashSet<Resource>();
87         
88         // runtime attributes
89         
90         private ReadGraph g;
91         private Layer0 b;
92         
93         public GraphComparator(Resource r1, Resource r2) {
94                 this.r1 = r1;
95                 this.r2 = r2;
96                 comparator = new TypeComparator();
97         }
98         
99         public GraphComparator(Resource r1, Resource r2, ResourceComparator comparator) {
100                 this.r1 = r1;
101                 this.r2 = r2;
102                 this.comparator = comparator;
103         }
104         
105         ArrayList<Statement> ss1 = new ArrayList<Statement>();
106         ArrayList<Statement> ss2 = new ArrayList<Statement>();
107         
108         
109         public Comparator<Resource> getResourceComparator() {
110                 return rcomp;
111         }
112         
113         public Comparator<Statement> getStatementComparator() {
114                 return scomp;
115         }
116         
117         public Resource getR1() {
118                 return r1;
119         }
120         
121         public Resource getR2() {
122                 return r2;
123         }
124         
125         public void addTraversed(Resource rel) {
126                 traversed.add(rel);
127         }
128         
129         public void addTraversed(Collection<Resource> rels) {
130                 traversed.addAll(rels);
131         }
132         
133         public void addNonTraversed(Resource rel) {
134                 nonTraversed.add(rel);
135         }
136         
137         public void addNonTraversed(Collection<Resource> rels) {
138                 nonTraversed.addAll(rels);
139         }
140         
141         public void addTested(Resource rel) {
142                 tested.add(rel);
143         }
144         
145         public void addTested(Collection<Resource> rels) {
146                 tested.addAll(rels);
147         }
148         
149         public void addNonTested(Resource rel) {
150                 nonTested.add(rel);
151         }
152         
153         public void addNonTested(Collection<Resource> rels) {
154                 nonTested.addAll(rels);
155         }
156         
157         public void addComparableResources(Resource r1, Resource r2) {
158                 if (DEBUG)
159                         System.out.println("Preset " + r1 + " = " + r2);
160                 comparableResources.map(r1, r2);
161         }
162         
163         public void addComparableResources(BijectionMap<Resource, Resource> matching) {
164                 if (DEBUG) {
165                         for (Entry<Resource, Resource> entry : matching.getEntries())
166                                 System.out.println("Preset " + entry.getKey() + " = " + entry.getValue());
167                 }
168                 comparableResources.addAll(matching);
169         }
170         
171         public void addStrong(Resource r) {
172                 strong.add(r);
173         }
174         
175         public void addStrong(Collection<Resource> rels) {
176                 strong.addAll(rels);
177         }
178         
179         public void addNonMatchedLeft(Resource r) {
180                 nonMatchedLeft.add(r);
181         }
182         
183         public void addNonMatchedRight(Resource r) {
184                 nonMatchedRight.add(r);
185         }
186         
187         public void test(ReadGraph g) throws DatabaseException {
188                 this.g = g;
189                 this.b = Layer0.getInstance(g);
190                 comparator.setComparator(this);
191                 comparator.initialize(g, r1, r2);
192                 
193                 Stack<Resource> objectsLeft = new Stack<Resource>();
194                 Stack<Resource> objectsRight = new Stack<Resource>();
195                 objectsLeft.push(r1);
196                 objectsRight.push(r2);
197                 
198                 Set<Statement> unreliableLeft = new HashSet<Statement>();
199                 Set<Statement> unreliableRight = new HashSet<Statement>();
200                 
201                 boolean changed = false;
202                 while (true) {
203                         if (objectsLeft.isEmpty() && !changed)
204                                 break;
205                         changed = false;
206                         
207                         // process compares objects that are identified and searches for more resources to process. 
208                         changed |= process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
209                         // process unreliable handles cases where unidentified statements subject and object have been identified 
210                         changed |= processUnreliable(unreliableLeft, unreliableRight);
211                         // process unreliable handles cases where unidentified resources have path of length one to identified resource
212                         changed |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
213                         if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
214                                 changed |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
215                         }
216                         if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
217                                 // comparison is ending, but we have still unprocessed unidentified resources left.
218                                 // These cases have longer path than one to identified objects.
219                                 changed |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
220                         }
221                         
222                 }
223                 for (Statement s : unreliableLeft) {
224                         if (!comparableStatements.containsLeft(s)) {
225                                 if (DEBUG) System.out.println("Unreliable Object deletion " + printStatement(g,s));
226                                 addDeletion(s);
227                         }
228                 }
229                 for (Statement s : unreliableRight) {
230                         if (!comparableStatements.containsRight(s)) {
231                                 if (DEBUG) System.out.println("Unreliable Object addition " + printStatement(g,s));
232                                 addAddition(s);
233                         }
234                 }
235         }
236         
237         public void test(Session session) throws DatabaseException {
238                 test(session, r1, r2);
239         }
240         
241         public void test(Session session, Resource r1, Resource r2) throws DatabaseException {
242                 
243                 comparator.setComparator(this);
244                 
245                 session.syncRequest(new ReadRequest() {
246             
247             @Override
248             public void run(ReadGraph graph) throws DatabaseException {
249                 comparator.initialize(graph, r1, r2);
250             }
251         });
252                 
253                 addComparable(r1, r2);
254                 
255                 final Stack<Resource> objectsLeft = new Stack<Resource>();
256                 final Stack<Resource> objectsRight = new Stack<Resource>();
257                 objectsLeft.push(r1);
258                 objectsRight.push(r2);
259                 
260                 final Set<Statement> unreliableLeft = new HashSet<Statement>();
261                 final Set<Statement> unreliableRight = new HashSet<Statement>();
262                 
263                 boolean changed = false;
264                 while (true) {
265                         if (objectsLeft.isEmpty() && !changed)
266                                 break;
267                         changed = session.syncRequest(new Read<Boolean>() {
268                                 
269                                 @Override
270                                 public Boolean perform(ReadGraph graph) throws DatabaseException {
271                                         g = graph;
272                                         b = Layer0.getInstance(graph);
273                                         // process compares objects that are identified and searches for more resources to process. 
274                                         boolean c = process(objectsLeft, objectsRight, unreliableLeft, unreliableRight);
275                                         // process unreliable handles cases where unidentified statements subject and object have been identified 
276                                         c |= processUnreliable(unreliableLeft, unreliableRight);
277                                         // process unreliable handles cases where unidentified resources have path of length one to identified resource
278                                         c |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
279                                         if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
280                                                 c |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight);
281                                         }
282                                         if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
283                                                 // comparison is ending, but we have still unprocessed unidentified resources left.
284                                                 // These cases have longer path than one to identified objects.
285                                                 c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
286                                         }
287                                         if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) {
288                                                 // comparison is ending, but we have still unprocessed unidentified resources left.
289                                                 // These cases have longer path than one to identified objects.
290                                                 c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight);
291                                         }
292                                         return c;
293                                 }
294                         });
295                         
296                         
297                         
298                 }
299                 for (Statement s : unreliableLeft) {
300                         if (!comparableStatements.containsLeft(s))
301                                 addDeletion(s);
302                 }
303                 for (Statement s : unreliableRight) {
304                         if (!comparableStatements.containsRight(s))
305                                 addAddition(s);
306                 }
307                 
308                 
309         }
310         
311         private boolean process(Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
312                 List<Statement> ss1 = new ArrayList<Statement>();
313                 List<Statement> ss2 = new ArrayList<Statement>();
314                 boolean didSomething = false;
315                 while (!objectsLeft.isEmpty()) {
316                         Resource r1 = objectsLeft.pop();
317                         Resource r2 = objectsRight.pop();
318                         
319                         if (r1.equals(r2))
320                                 continue;
321                         if (processedResources.contains(r1))
322                                 continue;
323                         processedResources.add(r1);
324                         
325                 
326                         if((comparableResources.containsLeft(r1)||comparableResources.containsRight(r2)) && !comparableResources.contains(r1, r2)) {
327                                 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.");
328                         }
329                         addComparable(r1, r2);
330                         didSomething = true;
331                         //System.out.println("test " + NameUtils.getSafeName(g, r1) + " " + NameUtils.getSafeName(g, r2));
332                         compareProps(r1, r2);
333                         
334                         for (Resource rel : tested) {
335                                 ss1.addAll(g.getStatements(r1, rel));
336                                 ss2.addAll(g.getStatements(r2, rel));
337                                 ss1 = filterAsserted(r1, ss1);
338                                 ss2 = filterAsserted(r2, ss2);
339                                 ss1 = filterTraversed(ss1);
340                                 ss2 = filterTraversed(ss2);
341                                 ss1 = filterNonTested(ss1);
342                                 ss2 = filterNonTested(ss2);
343                                 ss1 = filter(Collections.singletonList(b.HasProperty), ss1);
344                                 ss2 = filter(Collections.singletonList(b.HasProperty), ss2);
345                                 
346                                 compareStatements(ss1, ss2, null, null,null,null);
347                                 ss1.clear();
348                                 ss2.clear();
349                         }
350                         
351                         for (Resource rel : traversed) {
352                                 ss1.addAll(g.getStatements(r1, rel));
353                                 ss2.addAll(g.getStatements(r2, rel));
354                                 ss1 = filterAsserted(r1, ss1);
355                                 ss2 = filterAsserted(r2, ss2);
356                                 ss1 = filterNonTraversed(ss1);
357                                 ss2 = filterNonTraversed(ss2);
358                                 compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
359                                 ss1.clear();
360                                 ss2.clear();
361                                 
362                         }
363                 }
364                 return didSomething;
365         }
366         
367         private boolean processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight) throws DatabaseException {
368                 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
369                 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
370                 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
371                 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
372                 boolean didSomething = false;
373                 for (Statement s : unreliableLeft) {
374                         subjectLeft.add(s.getSubject(),s);
375                         objectLeft.add(s.getObject(),s);
376                 }
377                 for (Statement s : unreliableRight) {
378                         subjectRight.add(s.getSubject(),s);
379                         objectRight.add(s.getObject(),s);
380                 }
381                 
382                 for (Resource left : subjectLeft.getKeys()) {
383                         Resource right = comparableResources.getRight(left);
384                         if (right == null)
385                                 continue;
386                         for (Statement leftS : subjectLeft.getValues(left)) {
387                                 Resource leftO = leftS.getObject();
388                                 if (!unreliableLeft.contains(leftS))
389                                         continue;
390                                 Resource rightO = comparableResources.getRight(leftO);
391                                 if (rightO == null)
392                                         continue;
393                                 for (Statement rightS : subjectRight.getValues(right)) {
394                                         if (!rightS.getObject().equals(rightO))
395                                                 continue;
396                                         if (!unreliableRight.contains(rightS))
397                                                 continue;
398                                         if (leftS.getPredicate().equals(rightS.getPredicate()) ||
399                                                 comparableResources.contains(leftS.getPredicate(), rightS.getPredicate())) {
400                                                 unreliableLeft.remove(leftS);
401                                                 unreliableRight.remove(rightS);
402                                                 addComparable(leftS, rightS);
403                                                 didSomething = true;
404                                         }
405                                 }
406                         }               
407                 }
408                 return didSomething;
409         }
410         
411         private boolean processUnreliable(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
412                 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
413                 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
414                 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
415                 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
416                 boolean didSomething = false;
417                 for (Statement s : unreliableLeft) {
418                         subjectLeft.add(s.getSubject(),s);
419                         objectLeft.add(s.getObject(),s);
420                 }
421                 for (Statement s : unreliableRight) {
422                         subjectRight.add(s.getSubject(),s);
423                         objectRight.add(s.getObject(),s);
424                 }
425                 
426                 for (Resource ol : objectLeft.getKeys()) {
427                         // all statements to the left side object
428                         List<Statement> left = objectLeft.getValues(ol);
429                         // all subjects that have statements to the left side object (ol)
430                         Set<Resource> sLeft = new HashSet<Resource>();
431                         // all matching subjects on the right side
432                         Set<Resource> sRight = new HashSet<Resource>();
433                         for (Statement s : left) {
434                                 sLeft.add(s.getSubject());
435                                 sRight.add(comparableResources.getRight(s.getSubject()));
436                         }
437                         
438                         // check if object left can be reliably identified by available statements
439                         // if there are any objects on the left side with similar statements, object left cannot be mapped.
440                         boolean hasSimilar = false;
441                         MapList<Resource, Statement> comparableOLeft = new MapList<Resource, Statement>();
442                         for (Resource sl : sLeft) {
443                                 for (Statement s : subjectLeft.getValues(sl)) {
444                                         if (!s.getObject().equals(ol)) {
445                                                 comparableOLeft.add(s.getObject(),s);
446                                         }
447                                 }
448                         }
449                         
450                         compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
451                         
452                         for (Resource similarOl : comparableOLeft.getKeys()) {
453                                 List<Statement> similarLeft = comparableOLeft.getValues(similarOl);
454                                 if (similarLeft.size() == left.size()) {
455                                         boolean useL[] = new boolean[left.size()];
456                                         boolean useSL[] = new boolean[left.size()];
457                                         for (int i = 0; i < left.size(); i++) {
458                                                 useL[i] = false;
459                                                 useSL[i] = false;
460                                         }
461                                         for (int i = 0; i < left.size(); i++) {
462                                                 for (int j = 0; j < left.size(); j++) {
463                                                         if (useSL[j])
464                                                                 continue;
465                                                         // compare predicates
466                                                         Resource pl = left.get(i).getPredicate();
467                                                         Resource psl = similarLeft.get(j).getPredicate();
468                                                         if (pl.equals(psl)) {
469                                                                 // compare objects (unreliable result is interpreted as positive match)
470
471                                                                 int comp = comparator.compare(g, left.get(i).getObject(), similarLeft.get(j).getObject(), true);
472                                                                 if (comp >= 0 && comp < ResourceComparator.NO_MATCH) {
473                                                                         useL[i] = true;
474                                                                         useSL[j] = true;
475                                                                         break;
476                                                                 }
477                                                         }
478                                                 }
479                                         }
480                                         boolean diff = false;
481                                         for (int i = 0; i < left.size(); i++) {
482                                                 if (!useL[i] || !useSL[i]) {
483                                                         diff = true;
484                                                 }
485                                         }
486                                         if (!diff) {
487                                                 hasSimilar = true;
488                                                 break;
489                                         }
490                                 }
491                         }
492                         
493                         if (hasSimilar)
494                                 continue;
495                                 
496                         
497                         // all objects that subjects on the right side point to. Object left has its matching resource among these, if it has matching resource
498                         MapList<Resource,Statement> possibleOR = new MapList<Resource, Statement>();
499                         for (Resource sr : sRight) {
500                                 for (Statement s : subjectRight.getValues(sr))
501                                         possibleOR.add(s.getObject(),s);
502                         }
503                         
504                         // filter possible right side objects to those that have same amount of statements as the left side object
505                         for (Resource or : possibleOR.getKeys().toArray(new Resource[possibleOR.getKeys().size()])) {
506                                 List<Statement> right = possibleOR.getValues(or);
507                                 if (right.size() != left.size())
508                                         possibleOR.remove(or);
509                                         
510                         }
511                         
512                         // check for matching statements (comparable subjects, matching predicates)
513                         MapList<Resource,Statement> matchingOR = new MapList<Resource, Statement>(); // list of objects that have matching statements
514                         Map<Resource,Pair<int[], int[]>> matchingStatements = new HashMap<Resource, Pair<int[], int[]>>(); // matching statements
515                         for (Resource or : possibleOR.getKeys()) {
516                                 List<Statement> right = possibleOR.getValues(or);
517                                 int iLeft[] = new int[left.size()];
518                                 int iRight[] = new int[right.size()];
519                                 
520                                 for (int i = 0; i < left.size(); i++) {
521                                         iLeft[i] = -1;
522                                         iRight[i] = -1;
523                                 }
524                                 
525                                 for (int l = 0; l < left.size(); l++) {
526                                         Statement ls = left.get(l);
527                                         for (int r = 0; r < right.size(); r++) {
528                                                 if (iRight[r] >= 0)
529                                                         continue;
530                                                 Statement rs = right.get(r);
531                                                 if (!comparableResources.contains(ls.getSubject(), rs.getSubject()))
532                                                         continue;
533                                                 if ((comparableResources.containsLeft(ls.getObject()) || comparableResources.containsRight(rs.getObject())) && !comparableResources.contains(ls.getObject(), rs.getObject()))
534                                                         continue;
535                                                 if (rcomp.compare(ls.getPredicate(),rs.getPredicate()) == 0) {
536                                                         // compare objects (unreliable result is not accepted)
537                                                         int comp = comparator.compare(g, ls.getObject(), rs.getObject());
538                                                         if (comp > 0 && comp < Integer.MAX_VALUE) {
539                                                                 iLeft[l] = r;
540                                                                 iRight[r] = l;
541                                                                 break;
542                                                         }
543                                                         break;
544                                                 }
545                                         }
546                                         
547                                 }
548                                 boolean success = true;
549                                 for (int i = 0; i < left.size(); i++) {
550                                         if (iLeft[i] < 0) {
551                                                 success = false;
552                                                 break;
553                                         }
554                                         if (iRight[i] < 0) {
555                                                 success = false;
556                                                 break;
557                                         }
558                                                 
559                                 }
560                                 if (success) {
561                                         for (Statement s : right) 
562                                                 matchingOR.add(or,s);
563                                         matchingStatements.put(or, new Pair<int[], int[]>(iLeft, iRight));
564                                 }
565                         }
566                         // if there is only one matching right side object, we have found a match 
567                         if (matchingOR.getKeySize() == 1) {
568                                 Resource or = matchingOR.getKeys().iterator().next();
569                                 List<Statement> right = matchingOR.getValues(or);
570                                 Pair<int[], int[]> indices = matchingStatements.get(or);
571                                 
572                                 objectsLeft.add(ol);
573                                 objectsRight.add(or);
574                                 addComparable(ol, or);
575                                 didSomething = true;
576                                 for (int l = 0; l < left.size(); l++) {
577                                         int r = indices.first[l];
578                                         Statement sl = left.get(l);
579                                         Statement sr = right.get(r);
580                                         addComparable(sl, sr);
581                                         unreliableLeft.remove(sl);
582                                         unreliableRight.remove(sr);
583                                 }
584                                 
585                         } else {
586                                 
587                         }
588
589                 }
590                 return didSomething;
591                 
592                 
593         }
594         
595         private boolean processUnreliable2(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
596                 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
597                 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
598                 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
599                 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
600                 boolean didSomething = false;
601                 for (Statement s : unreliableLeft) {
602                         subjectLeft.add(s.getSubject(),s);
603                         objectLeft.add(s.getObject(),s);
604                 }
605                 for (Statement s : unreliableRight) {
606                         subjectRight.add(s.getSubject(),s);
607                         objectRight.add(s.getObject(),s);
608                 }
609                 
610                 for (Resource ol : objectLeft.getKeys()) {
611                         // all statements to the left side object
612                         List<Statement> left = objectLeft.getValues(ol);
613                         // all subjects that have statements to the left side object (ol)
614                         Set<Resource> sLeft = new HashSet<Resource>();
615                         // all matching subjects on the right side
616                         Set<Resource> sRight = new HashSet<Resource>();
617                         for (Statement s : left) {
618                                 sLeft.add(s.getSubject());
619                                 sRight.add(comparableResources.getRight(s.getSubject()));
620                         }
621                         
622                         if (sLeft.size() == 1 && sRight.size() == 1) {
623                                 List<Statement> ss1 = new ArrayList<Statement>(subjectLeft.getValues(sLeft.iterator().next()));
624                                 List<Statement> ss2 = new ArrayList<Statement>(subjectRight.getValues(sRight.iterator().next()));
625                                 
626                                 int count = comparableStatements.size();
627                                 compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight);
628                                 if (comparableStatements.size() > count) {
629                                         didSomething = true;
630                                         for (Entry<Statement, Statement> entry : comparableStatements.getEntries()) {
631                                                 unreliableLeft.remove(entry.getKey());
632                                                 unreliableRight.remove(entry.getValue());
633                                         }
634                                 }
635                         }
636                 }
637                 return didSomething;
638         }
639         
640         private boolean processUnreliableDeep(Set<Statement> unreliableLeft, Set<Statement> unreliableRight, Stack<Resource> objectsLeft, Stack<Resource> objectsRight) throws DatabaseException {
641                 MapList<Resource,Statement> subjectLeft = new MapList<Resource, Statement>();
642                 MapList<Resource,Statement> subjectRight = new MapList<Resource, Statement>();
643                 MapList<Resource,Statement> objectLeft = new MapList<Resource, Statement>();
644                 MapList<Resource,Statement> objectRight = new MapList<Resource, Statement>();
645                 boolean didSomething = false;
646                 for (Statement s : unreliableLeft) {
647                         subjectLeft.add(s.getSubject(),s);
648                         objectLeft.add(s.getObject(),s);
649                 }
650                 for (Statement s : unreliableRight) {
651                         subjectRight.add(s.getSubject(),s);
652                         objectRight.add(s.getObject(),s);
653                 }
654                 for (Resource ol : objectLeft.getKeys()) {
655                         Set<Path> pathsLeft = new HashSet<Path>();
656                         for (Resource rel : traversed) {
657                                 pathsLeft.addAll(Path.create(g.getStatements(ol, rel)));
658                         }
659                         while (true) {
660                                 expand(pathsLeft);
661                                 if (pathsLeft.size() == 0)
662                                         break;
663                                 Collection<Path> endPaths = new ArrayList<Path>(1);
664                                 for (Path p : pathsLeft) {
665                                         if (comparableResources.containsLeft(p.getEnd())) {
666                                                 endPaths.add(p);
667                                         }
668                                 }
669                                 if (endPaths.size() > 0) {
670                                         pathsLeft.clear();
671                                         pathsLeft.addAll(endPaths);
672                                         break;
673                                 }       
674                         }
675                         if (pathsLeft.size() > 0) {
676                                 Resource sl = objectLeft.getValues(ol).get(0).getSubject();
677                                 Resource sr = comparableResources.getRight(sl);
678                                 Collection<Resource> possibleOR = new ArrayList<Resource>();
679                                 for (Statement s : subjectRight.getValues(sr)) {
680                                         possibleOR.add(s.getObject());
681                                 }
682                                 Map<Resource,Set<Path>> matchingPaths = new HashMap<Resource, Set<Path>>();
683                                 for (Resource or : possibleOR) {
684                                         Set<Path> possiblePathsRight = new HashSet<Path>();
685                                         for (Path leftPath : pathsLeft) {
686                                                 possiblePathsRight.addAll(findComparableRight(leftPath, or));
687                                         }
688                                         if (hasMatchingPaths(pathsLeft, possiblePathsRight)) {
689                                                 matchingPaths.put(or, possiblePathsRight);
690                                         }
691                                 }
692                                 if (matchingPaths.size() > 0) {
693                                         if (matchingPaths.size() == 1) {
694                                                 didSomething = true;
695                                                 Resource or = matchingPaths.keySet().iterator().next();
696                                                 
697                                                 objectsLeft.add(ol);
698                                                 objectsRight.add(or);
699                                                 addComparable(ol, or);
700                                                 Collection<Statement> statementsLeft = objectLeft.getValues(ol);
701                                                 Collection<Statement> statementsRight = objectRight.getValues(or);
702                                                 unreliableLeft.removeAll(statementsLeft);
703                                                 unreliableRight.removeAll(statementsRight);
704                                                 BijectionMap<Path,Path> map = getMatchingPaths(pathsLeft, matchingPaths.get(or));
705                                                 for (Path left : map.getLeftSet()) {
706                                                         Path right = map.getRight(left);
707                                                         for (int i = 0; i < left.getLength(); i++) {
708                                                                 addComparable(left.getStatements().get(i),right.getStatements().get(i));
709                                                         }
710                                                 }
711                                         } 
712                                 }
713                         }
714                         
715                 }
716                 return didSomething;
717                 
718         }
719         
720         private boolean hasMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) throws DatabaseException {
721                 if (leftPaths.size() != rightPaths.size())
722                         return false;
723                 BijectionMap<Path,Path> map = getMatchingPaths(leftPaths, rightPaths);
724                 return map.size() == leftPaths.size();
725         }
726         
727         private BijectionMap<Path,Path> getMatchingPaths(Set<Path> leftPaths, Set<Path> rightPaths) throws DatabaseException {
728                 BijectionMap<Path,Path> map = new BijectionMap<Path, Path>();
729                 for (Path leftPath : leftPaths) {
730                         for (Path rightPath : rightPaths) {
731                                 if (map.containsRight(rightPath))
732                                         continue;
733                                 if (leftPath.getLength() != rightPath.getLength())
734                                         continue;
735                                 if (comparableResources.contains(leftPath.getEnd(), rightPath.getEnd())) {
736                                         boolean match = true;
737                                         for (int i = 0; i < leftPath.getLength(); i++) {
738                                                 Statement sl = leftPath.getStatements().get(i);
739                                                 Statement sr = rightPath.getStatements().get(i);
740                                                 if (!sl.getPredicate().equals(sr.getPredicate()) && !comparableResources.contains(sl.getPredicate(), sr.getPredicate())) {
741                                                         match = false;
742                                                         break;
743                                                 }
744                                                 if ((getComparableResources().containsLeft(sl.getObject()) || getComparableResources().containsRight(sr.getObject())) && !getComparableResources().contains(sl.getObject(), sr.getObject())) {
745                                                         match = false;
746                                                         break;
747                                                 }
748                                                 if (comparator.compare(g, sl.getObject(), sr.getObject()) == ResourceComparator.NO_MATCH) {
749                                                         match = false;
750                                                         break;
751                                                 }
752                                         }
753                                         if (match) {
754                                                 map.map(leftPath, rightPath);
755                                                 break;
756                                         }
757                                 }
758                         }
759                 }
760                 return map;
761         }
762         
763         private void expand(Set<Path> paths) throws DatabaseException {
764                 Set<Path> stepPathsLeft = new HashSet<Path>();
765                 if (paths.size() == 0)
766                         return;
767                 int length = paths.iterator().next().getLength() + 1;
768                 for (Path p : paths) {
769                         for (Resource rel : traversed) {
770                                 stepPathsLeft.addAll(Path.expand(p,g.getStatements(p.getEnd(), rel)));
771                         }
772                 }
773                 paths.clear();
774                 for (Path p : stepPathsLeft) {
775                         if (p.getLength() == length)
776                                 paths.add(p);
777                 }
778         }
779         
780         private Collection<Path> findComparableRight(Path leftPath, Resource beginRight) throws DatabaseException {
781                 Set<Path> rightPaths = new HashSet<Path>();
782                 rightPaths.addAll(Path.create(g.getStatements(beginRight, getRight(leftPath.getStatements().get(0).getPredicate()))));
783                 for (int i = 1; i < leftPath.getLength(); i++) {
784                         if (rightPaths.size() == 0)
785                                 return rightPaths;
786                         Set<Path> stepPaths = new HashSet<Path>();
787                         for (Path p : rightPaths) {
788                                 stepPaths.addAll(Path.expand(p, g.getStatements(p.getEnd(), getRight(leftPath.getStatements().get(i).getPredicate()))));
789                         }
790                         rightPaths.clear();
791                         for (Path p : stepPaths)
792                                 if (p.getLength() == i+1) 
793                                         rightPaths.add(p);
794                 }
795                 return rightPaths;
796                 
797         }
798         
799         private Resource getRight(Resource r) {
800                 if (comparableResources.containsLeft(r))
801                         return comparableResources.getRight(r);
802                 return r;
803         }
804         
805
806         
807         public BijectionMap<Statement, Statement> getComparableStatements() {
808                 return comparableStatements;
809         }
810         
811         public BijectionMap<Resource, Resource> getComparableResources() {
812                 return comparableResources;
813         }
814         
815         public GraphChanges getChanges() {
816                 return new GraphChanges(r1,r2,changes1,changes2,modifications,comparableResources);
817         }
818         
819         public List<Statement> getChanges1() {
820                 return changes1;
821         }
822         
823         public List<Statement> getChanges2() {
824                 return changes2;
825         }
826         
827         private void addComparable(Statement left, Statement right) throws DatabaseException {
828                 addComparable(left.getObject(), right.getObject());
829                 comparableStatements.map(left, right);
830         }
831         
832         private void addComparable(Resource left, Resource right) throws DatabaseException {
833                 if(!comparableResources.contains(left, right)) {
834                         if (comparableResources.containsLeft(left)||comparableResources.containsRight(right)) {
835                                 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.");
836                         } else {
837                                 if (DEBUG) System.out.println(left + " = " + right);
838                                 comparableResources.map(left, right);
839                         }
840                 }
841                 
842         }
843         
844         public List<Statement> filterAsserted(Resource r, Collection<Statement> in) throws DatabaseException {
845                 List<Statement> out = new ArrayList<Statement>();
846                 for (Statement s : in) {
847                         if (!s.isAsserted(r))
848                                 out.add(s);
849                         
850                 }
851                 return out;
852         }
853         
854         public List<Statement> filterAssertedDuplicates(Resource r, List<Statement> in) throws DatabaseException {
855                 List<Statement> out = new ArrayList<Statement>();
856                 for (int i = 0; i < in.size(); i++) {
857                         Statement s = in.get(i);
858                         if (!s.isAsserted(r))
859                                 out.add(s);
860                         else {
861                                 boolean has = false;
862                                 if (i > 0 && in.get(i-1).getPredicate().equals(s.getPredicate()))
863                                         has = true;
864                                 else if (i < in.size()-1 && in.get(i+1).getPredicate().equals(s.getPredicate()))
865                                         has = true;
866                                 if (!has)
867                                         out.add(s);
868                         }
869                         
870                 }
871                 return out;
872         }
873         
874         
875
876         private String printStatement(ReadGraph graph, Statement s) throws DatabaseException {
877                 return NameUtils.getSafeName(graph, s.getSubject()) + " " + NameUtils.getSafeName(graph, s.getPredicate()) + " " + NameUtils.getSafeName(graph, s.getObject());
878         }
879         
880         private List<Statement> filterTraversed(List<Statement> in) throws DatabaseException {
881                 return filter(traversed, in);
882         }
883         
884         private List<Statement> filterNonTested(List<Statement> in) throws DatabaseException {
885                 return filter(nonTested, in);
886         }
887         
888         private List<Statement> filterNonTraversed(List<Statement> in) throws DatabaseException {
889                 return filter(nonTraversed, in);
890         }
891         
892         private List<Statement> filter(Collection<Resource> toFilter, List<Statement> in) throws DatabaseException {
893                 if (toFilter.size() == 0)
894                         return in;
895                 List<Statement> out = new ArrayList<Statement>();
896                 for (Statement s : in) {
897                         boolean usable = true;
898                         for (Resource r : toFilter) {
899                                 if (g.isSubrelationOf(s.getPredicate(),r)) {
900                                         usable = false;
901                                         break;
902                                 }
903                         }
904                         if (usable) {
905                                 out.add(s);
906                         }
907                         
908                 }
909                 return out;
910         }
911         
912         
913         private void addDeletion(Statement s) {
914                 if (!changes1Set.contains(s)) {
915                         if (DEBUG) System.out.println("- " +s.getSubject() + " " + s.getPredicate() + " " + s.getObject()) ;
916                         changes1Set.add(s);
917                         changes1.add(s);
918                 }
919         }
920         
921         private void addAddition(Statement s) {
922                 if (!changes2Set.contains(s)) {
923                         changes2Set.add(s);
924                         if (DEBUG) System.out.println("+ " +s.getSubject() + " " + s.getPredicate() + " " + s.getObject()) ;
925                         changes2.add(s);
926                 }
927         }
928         
929         private void addModification(Resource left, Statement leftstm, Resource right, Statement rightstm) {
930                 Modification mod = new Modification(left, right, leftstm, rightstm);
931                 if (!modificationsSet.contains(mod)) {
932                         modificationsSet.add(mod);
933                         modifications.add(mod);
934                 }
935         }
936         
937         public void sortStatement(List<Statement> list1, List<Statement> list2) {
938                 sortStatement(list1, list2, scomp);
939         }
940         
941         public void sortStatement(List<Statement> list1, List<Statement> list2, Comparator<Statement> scomp) {
942                 Collections.sort(list1,scomp);
943                 Collections.sort(list2,scomp);
944                 
945                 List<Statement> sorted1 = new ArrayList<Statement>(list1.size());
946                 List<Statement> sorted2 = new ArrayList<Statement>(list2.size());
947                 sorted1.addAll(list1);
948                 sorted2.addAll(list2);
949                 
950                 int ss1 = 0;
951                 int ss2 = 0;
952                 for (int i = 0; i < list1.size(); ) {
953                         Statement s1 = list1.get(i);
954                         int same1 = sameRel(list1, i);  
955                         for (int j = 0; j < list2.size(); j++) {
956                                 Statement s2 = list2.get(j);
957                                 if (scomp.compare(s1, s2) == 0) {
958                                         int same2 = sameRel(list2, j);
959                                         copy(sorted1,ss1,list1,i,same1);
960                                         ss1 += same1;
961                                         copy(sorted2,ss2,list2,j,same2);
962                                         ss2 += same2;
963                                         break;
964                                 }
965                         }
966                         i+= same1;
967                 }
968                 if (ss1 < sorted1.size()) {
969                         for (Statement s : list1) {
970                                 if (!sorted1.contains(s)) {
971                                         sorted1.set(ss1,s);
972                                         ss1++;
973                                 }
974                         }
975                 }
976                 if (ss2 < sorted2.size()) {
977                         for (Statement s : list2) {
978                                 if (!sorted2.contains(s)) {
979                                         sorted2.set(ss2,s);
980                                         ss2++;
981                                 }
982                         }
983                 }
984                 
985                 list1.clear();
986                 list2.clear();
987                 list1.addAll(sorted1);
988                 list2.addAll(sorted2);
989         }
990         
991         public <T> void copy(List<T> to, int toIndex, List<T> from, int fromIndex, int amount) {
992                 for (int i = 0; i <  amount; i++) {
993                         to.set(toIndex + i, from.get(fromIndex+ i));
994                 }
995         }
996         
997         public void sortResource(List<Resource> list1, List<Resource> list2) {
998                 Collections.sort(list1,rcomp);
999                 int js = 0;
1000                 for (int i = 0; i < list1.size(); i++) {
1001                         Resource s1 = list1.get(i);
1002                         for (int j = js; j < list2.size(); j++) {
1003                                 Resource s2 = list2.get(j);
1004                                 if (rcomp.compare(s1, s2) == 0) {
1005                                         Resource t = list2.get(js);
1006                                         list2.set(js, s2);
1007                                         list2.set(j, t);
1008                                         break;
1009                                 }
1010                         }
1011                         js++;
1012                 }
1013         }
1014         
1015         private void compareStatements(List<Statement> ss1, List<Statement> ss2, Stack<Resource> objectsLeft, Stack<Resource> objectsRight, Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight) throws DatabaseException {
1016                 sortStatement(ss1, ss2);
1017                 
1018                 int i1 = 0;
1019                 int i2 = 0;
1020                 
1021                 while (true) {
1022                         if (i1 >= ss1.size()) {
1023                                 if (i2 >= ss2.size()) {
1024                                         break;
1025                                 } else {
1026                                         while (i2 < ss2.size()) {
1027                                                 if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i2)));
1028                                                 
1029                                                 addAddition(ss2.get(i2));
1030                                                 i2++;
1031                                         }
1032                                         break;
1033                                 }
1034                         } else if (i2 >= ss2.size()) {
1035                                 while (i1 < ss1.size()) {
1036                                         if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i1)));
1037                                         addDeletion(ss1.get(i1));
1038                                         i1++;
1039                                 }
1040                                 break;
1041                         }
1042                         int same1 = sameRel(ss1, i1);
1043                         int same2 = sameRel(ss2, i2);
1044                         int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate());
1045                         if (c == 0) {
1046                                 compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight);
1047                                 i1+=same1;
1048                                 i2+=same2;
1049                         } else if (c < 0) {
1050                                 for (int i = 0; i < same1; i++) {
1051                                         if (DEBUG) System.out.println("Compare Statements deletion " + printStatement(g,ss1.get(i+i1)));
1052                                         addDeletion(ss1.get(i+i1));
1053                                 }
1054                                 i1 += same1;
1055                         } else {
1056                                 for (int i = 0; i < same2; i++) {
1057                                         if (DEBUG) System.out.println("Compare Statements addition " + printStatement(g,ss2.get(i+i2)));
1058                                         addAddition(ss2.get(i+i2));
1059                                 }
1060                                 
1061                                 i2 += same2;
1062                         }
1063                 }
1064         }
1065         
1066
1067         
1068         private int sameRel(List<Statement> statements, int off) {
1069                 if (statements.size() <= off)
1070                         return 0;
1071                 int same = 1;
1072                 long id = statements.get(off).getPredicate().getResourceId();
1073                 for (int i = off+1; i <statements.size(); i++) {
1074                         if (statements.get(i).getPredicate().getResourceId() == id)
1075                                 same++;
1076                         else 
1077                                 break;
1078                 }
1079                 return same;
1080                 
1081         }
1082
1083         private int compareObject(Resource o1, Resource o2) throws DatabaseException {
1084                 if (o1.equals(o2))
1085                         return -1;
1086                 if (comparableResources.contains(o1, o2))
1087                         return (-1);
1088                 if (comparableResources.containsLeft(o1))
1089                         return Integer.MAX_VALUE;
1090                 if (comparableResources.containsRight(o2))
1091                         return Integer.MAX_VALUE;
1092                 if (nonMatchedLeft.contains(o1))
1093                         return Integer.MAX_VALUE;
1094                 if (nonMatchedRight.contains(o2))
1095                         return Integer.MAX_VALUE;
1096                 return comparator.compare(g, o1, o2);
1097         }
1098         
1099         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 {
1100                 boolean[] used1 = new boolean[len1];
1101                 for (int i = 0; i < used1.length; i++) {
1102                         used1[i] = false;
1103                 }
1104                 
1105                 boolean[] used2 = new boolean[len2];
1106                 for (int i = 0; i < used2.length; i++) {
1107                         used2[i] = false;
1108                 }
1109                                 
1110                 // left, right, difference
1111                 List<List<Integer>> differences = new ArrayList<List<Integer>>();
1112                 for (int i1 = off1; i1 < off1 + len1; i1++) {
1113                         Statement s1 = ss1.get(i1);
1114                         List<Integer> diff = new ArrayList<Integer>();
1115                         for (int i2 = off2; i2 < off2 + len2; i2++) {
1116                                 Statement s2 = ss2.get(i2);
1117                                 int d = compareObject(s1.getObject(), s2.getObject());
1118                                 if (d == 0) {
1119                                         for (Resource t : strong) {
1120                                                  if (s1.getPredicate().equals(t) || g.isSubrelationOf(s1.getPredicate(), t)) {
1121                                                          d = 1;
1122                                                          break;
1123                                                  }
1124                                         }
1125                                 }
1126                                 diff.add(d);
1127                         }
1128                         differences.add(diff);
1129                 }
1130                 // difference, left
1131                 MapList<Integer, Integer> priorities = new MapList<Integer, Integer>();
1132                 for (int i = 0; i < differences.size(); i++) {
1133                         List<Integer> list = differences.get(i);
1134                         for (int j = 0; j < list.size(); j++) {
1135                                 priorities.add(list.get(j), i);
1136                         }
1137                 }
1138                 
1139                 Integer[] pris = priorities.getKeys(new Integer[]{});
1140                 Arrays.sort(pris);
1141                 boolean matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, false);
1142                 if (matchFail) 
1143                         matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, objectsLeft == null);
1144                 if (unreliableLeft != null) {
1145                         if (matchFail) {
1146                                 // With match failure, statement matching was aborted. We must move all unmatched statements to unreliable.  
1147                                 for (Integer pri : pris) {
1148                                         if (pri == 0 || pri == Integer.MAX_VALUE)
1149                                                 continue;
1150                                         priorityProcessUnmatched(ss1, off1, len1, ss2, off2, len2, differences, priorities, used1, used2, unreliableLeft, unreliableRight, pri);
1151                                 }
1152                         }
1153                         // Zero means unreliable comparison result, move all unmatched to unreliable. 
1154                         if (org.simantics.utils.datastructures.Arrays.contains(pris, 0)) {
1155                                 priorityProcessUnmatched(ss1, off1, len1, ss2, off2, len2, differences, priorities, used1, used2, unreliableLeft, unreliableRight, 0);
1156                         }
1157                 } 
1158 //      Previous version processed 0 priority statements, even when unreliableLeft collection was null.
1159 //              The problem was that property relations were not filtered in process() from "tested" statements.                
1160 //              else {
1161 //                      if (org.simantics.utils.datastructures.Arrays.contains(pris, 0)) {
1162 //                              priorityProcessUnmatched(ss1, off1, len1, ss2, off2, len2, differences, priorities, used1, used2, unreliableLeft, unreliableRight, 0);
1163 //                      }
1164 //              }
1165                 // Report unmatched statements as changes.
1166                 for (int i1 = off1; i1 < off1 + len1; i1++) {
1167                         if (!used1[i1-off1]) {
1168                                 if (DEBUG) System.out.println("Compare Object deletion " + printStatement(g,ss1.get(i1)));
1169                                 addDeletion(ss1.get(i1));
1170                         }
1171                 }
1172                 for (int i2 = off2; i2 < off2 + len2; i2++) {
1173                         if (!used2[i2-off2]) {
1174                                 if (DEBUG) System.out.println("Compare Object addition " + printStatement(g,ss2.get(i2)));
1175                                 addAddition(ss2.get(i2));
1176                         }
1177                 }
1178         }
1179         
1180         /**
1181          * Moves unmatched statements to unreliable collections.
1182          * @param ss1
1183          * @param off1
1184          * @param len1
1185          * @param ss2
1186          * @param off2
1187          * @param len2
1188          * @param differences
1189          * @param priorities
1190          * @param used1
1191          * @param used2
1192          * @param unreliableLeft
1193          * @param unreliableRight
1194          * @param pri
1195          */
1196         private void priorityProcessUnmatched(List<Statement> ss1, int off1, int len1, List<Statement> ss2, int off2, int len2,List<List<Integer>> differences,MapList<Integer, Integer> priorities,boolean used1[],boolean used2[], Collection<Statement> unreliableLeft, Collection<Statement> unreliableRight, int pri) {
1197                 Set<Statement> s1s = new HashSet<Statement>();
1198                 Set<Statement> s2s = new HashSet<Statement>();
1199                 Set<Integer> s1i = new HashSet<Integer>();
1200                 Set<Integer> s2i = new HashSet<Integer>();
1201                 List<Integer> i1s = priorities.getValues(pri);
1202                 for (Integer i1 : i1s) {
1203                         if (used1[i1])
1204                                 continue;
1205                         List<Integer> i2diff = differences.get(i1);
1206                         for (int i2 = 0; i2 < i2diff.size(); i2++) {
1207                                 if (i2diff.get(i2) == pri) {
1208                                         if (used2[i2])
1209                                                 continue;
1210                                         Statement s1  = ss1.get(i1+off1);
1211                                         Statement s2  = ss2.get(i2+off2);
1212                                         s1s.add(s1);
1213                                         s2s.add(s2);
1214                                         s1i.add(i1);
1215                                         s2i.add(i2);
1216                                 }
1217                         }
1218                 }
1219                 if (unreliableLeft != null) {
1220                         unreliableLeft.addAll(s1s);
1221                         unreliableRight.addAll(s2s);
1222                 }
1223                 for (Integer i : s1i)
1224                         used1[i] = true;
1225                 for (Integer i : s2i)
1226                         used2[i] = true;
1227         }
1228         /**
1229          * Matches left and right side statements.
1230          * 
1231          * When there are two or more equally good matching objects, the behaviour depends on force flag.
1232          *   False:  Matching is aborted and matchFail is returned (method return true).
1233          *   True: equally good matches will be paired randomly. Method always returns false.
1234          * @param ss1
1235          * @param off1
1236          * @param len1
1237          * @param ss2
1238          * @param off2
1239          * @param len2
1240          * @param pris
1241          * @param differences
1242          * @param priorities
1243          * @param used1
1244          * @param used2
1245          * @param objectsLeft
1246          * @param objectsRight
1247          * @param force 
1248          * @return
1249          * @throws DatabaseException
1250          */
1251         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 {
1252                 boolean matchFail = false;
1253                 for (Integer pri : pris) {
1254                         if (pri == Integer.MAX_VALUE) {
1255
1256                         } else if (pri == 0) {
1257                                 
1258                         } else {
1259                                 List<Integer> i1s = priorities.getValues(pri);
1260                                 
1261                                 for (Integer i1 : i1s) {
1262                                         if (used1[i1])
1263                                                 continue;
1264                                         List<Integer> i2diff = differences.get(i1);
1265                                         List<Integer> matches = new ArrayList<Integer>();
1266                                         for (int i2 = 0; i2 < i2diff.size(); i2++) {
1267                                                 if (i2diff.get(i2) == pri) { 
1268                                                         if (used2[i2])
1269                                                                 continue;
1270                                                         matches.add(i2);
1271                                                 }
1272                                         }
1273                                         if (matches.size() == 1 || (force && matches.size() > 1)) {
1274                                                 int i2 = matches.get(0);
1275                                                 used1[i1] = true;
1276                                                 used2[i2] = true;
1277                                                 Statement s1  = ss1.get(i1+off1);
1278                                                 Statement s2  = ss2.get(i2+off2);
1279                                                 
1280                                                 if (objectsLeft != null) {
1281                                                         objectsLeft.add(s1.getObject());
1282                                                         objectsRight.add(s2.getObject());
1283                                                 } 
1284                                                 addComparable(s1, s2);
1285                                         } else if (matches.size() > 1) {
1286                                                 matchFail = true;
1287                                         }
1288                                 }
1289                                 if (matchFail)
1290                                         break;
1291                         }
1292                 }
1293                 return matchFail;
1294         }
1295
1296         
1297         /**
1298          * compares properties, assumes functional relations
1299          * @param r1
1300          * @param r2
1301          * @throws DatabaseException 
1302          */
1303         private void compareProps(Resource r1, Resource r2) throws DatabaseException {
1304                 if (DEBUG) System.out.println("compareProps " + r1 + " " + NameUtils.getSafeName(g, r1) + " " + r2 + " " + NameUtils.getSafeName(g, r2));
1305                 List<Statement> ss1 = new ArrayList<Statement>();
1306                 List<Statement> ss2 = new ArrayList<Statement>();
1307                 ss1.addAll(g.getStatements(r1, b.HasProperty));
1308                 ss2.addAll(g.getStatements(r2, b.HasProperty));
1309                 ss1 = filterNonTested(ss1);
1310                 ss2 = filterNonTested(ss2);
1311                 sortStatement(ss1, ss2);
1312                 // getStatements(r1, b.HasProperty) returns both instance and asserted statements for the same property relation. 
1313                 ss1 = filterAssertedDuplicates(r1, ss1);
1314                 ss2 = filterAssertedDuplicates(r2, ss2);
1315                 
1316                 int i1 = 0; 
1317                 int i2 = 0;
1318                 
1319                 while (true) {
1320                         if (i1 >= ss1.size()) {
1321                                 if (i2 >= ss2.size())
1322                                         break;
1323                                 else {
1324                                         while (i2 < ss2.size()) {
1325                                                 Statement s = ss2.get(i2);
1326                                                 if (DEBUG) System.out.println("Compare Prop diff2 " + printStatement(g,s));
1327                                                 if (!s.isAsserted(r2))
1328                                                         addAddition(s);
1329                                                 i2++;
1330                                         }
1331                                         break;
1332                                 }
1333                         } else if (i2 >= ss2.size()) {
1334                                 while (i1 < ss1.size()) {
1335                                         Statement s = ss1.get(i1);
1336                                         if (DEBUG) System.out.println("Compare Prop diff1 " + printStatement(g,s));
1337                                         if (!s.isAsserted(r1))
1338                                                 addDeletion(s);
1339                                         i1++;
1340                                 }
1341                                 break;
1342                         }
1343                         Statement s1 = ss1.get(i1);
1344                         Statement s2 = ss2.get(i2);
1345                         if (s1.isAsserted(r1) && s2.isAsserted(r2)) {
1346                                 i1++;
1347                                 i2++;
1348                                 continue;
1349                         }
1350                         int c = scomp.compare(s1, s2);
1351                         switch (c) {
1352                                 case 0:{
1353                                         boolean b1 = g.hasValue(s1.getObject());
1354                                         boolean b2 = g.hasValue(s2.getObject());
1355                                         boolean a1 = s1.isAsserted(r1);
1356                                         boolean a2 = s2.isAsserted(r2);
1357                                         if (b1 == b2) {
1358                                                 if (b1) {
1359                                                         // Literals
1360                                                         boolean eq = compareValue(g,b,s1.getObject(), s2.getObject());
1361                                                         if (!eq) {
1362                                                                 addModification(r1,s1,r2,s2);
1363                                                                 if (!a1 && !a2)
1364                                                                         addComparable(s1, s2);
1365                                                         }
1366                                                 } else {
1367                                                         // Non literal properties.
1368                                                         int comp = comparator.compare(g, s1.getObject(), s2.getObject());
1369                                                         if (comp == ResourceComparator.NO_MATCH) {
1370                                                                 addModification(r1,s1,r2,s2);
1371                                                         } else if (comp != ResourceComparator.EXACT_MATCH) {
1372                                                                 if (!s1.getObject().equals(s1.getSubject()) && !s2.getObject().equals(s2.getSubject())) {
1373                                                                         if (!a1 && !a2) {
1374                                                                                 // compare props matches objects, so we can call that only for non asserted statements
1375                                                                                 compareProps(s1.getObject(), s2.getObject());
1376                                                                         } else if (a1 && a2) {
1377                                                                                 // TODO : compare asserted statements?
1378                                                                         } else {
1379                                                                                 addModification(r1,s1,r2,s2);
1380                                                                         }
1381                                                                 } else {
1382                                                                         addModification(r1,s1,r2,s2);
1383                                                                 }
1384                                                         } else {
1385                                                                 // Exact match, nothing to do.
1386                                                                 if (!a1 && !a2)
1387                                                                         addComparable(s1, s2);
1388                                                         }
1389                                                 }
1390                                         } else {
1391                                                 addModification(r1,s1,r2,s2);
1392                                         }
1393                                         i1++;
1394                                         i2++;
1395                                         break;
1396                                 }
1397                                 case -1:{
1398                                         if (DEBUG) System.out.println("Compare Prop diff1s " + printStatement(g,s1));
1399                                         // Use modification, since deletions do not support asserted statements
1400                                         addModification(r1,s1,r2,null);
1401                                         //addDeletion(s1);
1402                                         i1++;
1403                                         break;
1404                                 }
1405                                         
1406                                 case 1:{
1407                                         if (DEBUG) System.out.println("Compare Prop diff2s " + printStatement(g,s2));
1408                                         // Use modification, since additions do not support asserted statements
1409                                         addModification(r1,null,r2,s2);
1410                                         //addAddition(s2);
1411                                         i2++;
1412                                         break;
1413                                 }
1414                         }
1415
1416                 }
1417                 
1418                 ss1.clear();
1419                 ss2.clear();
1420                 
1421         }
1422         
1423         public static boolean compareValue(ReadGraph g, Layer0 b, Resource r1, Resource r2) throws DatabaseException {
1424                 Resource t1 = g.getSingleType(r1);
1425                 Resource t2 = g.getSingleType(r2);
1426                 if (!t1.equals(t2))
1427                         return false;
1428                 if (t1.equals(b.Integer)) {
1429                         int v1 = g.getValue(r1, Bindings.INTEGER);
1430                         int v2 = g.getValue(r2, Bindings.INTEGER);
1431                         return v1 == v2;
1432                 } else if (t1.equals(b.Float)) {
1433                         float v1 = g.getValue(r1, Bindings.FLOAT);
1434                         float v2 = g.getValue(r2, Bindings.FLOAT);
1435                         return v1 == v2;
1436                 } else if (t1.equals(b.Double)) {
1437                         double v1 = g.getValue(r1, Bindings.DOUBLE);
1438                         double v2 = g.getValue(r2, Bindings.DOUBLE);
1439                         return v1 == v2;
1440                 } else if (t1.equals(b.String)) {
1441                         String v1 = g.getValue(r1, Bindings.STRING);
1442                         String v2 = g.getValue(r2, Bindings.STRING);
1443                         return v1.equals(v2);
1444                 } else if (t1.equals(b.Boolean)) {
1445                         boolean v1 = g.getValue(r1, Bindings.BOOLEAN);
1446                         boolean v2 = g.getValue(r2, Bindings.BOOLEAN);
1447                         return v1 == v2;
1448                 } else if (t1.equals(b.Byte)) {
1449                         byte v1 = g.getValue(r1, Bindings.BYTE);
1450                         byte v2 = g.getValue(r2, Bindings.BYTE);
1451                         return v1 == v2;
1452                 } else if (t1.equals(b.Long)) {
1453                         long v1 = g.getValue(r1, Bindings.LONG);
1454                         long v2 = g.getValue(r2, Bindings.LONG);
1455                         return v1 == v2;
1456                 } else if (t1.equals(b.IntegerArray)) {
1457                         int[] v1 = g.getValue(r1, Bindings.INT_ARRAY);
1458                         int[] v2 = g.getValue(r2, Bindings.INT_ARRAY);
1459                         return Arrays.equals(v1,v2);
1460                 } else if (t1.equals(b.FloatArray)) {
1461                         float[] v1 = g.getValue(r1, Bindings.FLOAT_ARRAY);
1462                         float[] v2 = g.getValue(r2, Bindings.FLOAT_ARRAY);
1463                         return Arrays.equals(v1,v2);
1464                 } else if (t1.equals(b.DoubleArray)) {
1465                         double[] v1 = g.getValue(r1, Bindings.DOUBLE_ARRAY);
1466                         double[] v2 = g.getValue(r2, Bindings.DOUBLE_ARRAY);
1467                         return Arrays.equals(v1,v2);
1468                 } else if (t1.equals(b.StringArray)) {
1469                         String[] v1 = g.getValue(r1, Bindings.STRING_ARRAY);
1470                         String[] v2 = g.getValue(r2, Bindings.STRING_ARRAY);
1471                         return Arrays.equals(v1,v2);
1472                 } else if (t1.equals(b.BooleanArray)) {
1473                         boolean[] v1 = g.getValue(r1, Bindings.BOOLEAN_ARRAY);
1474                         boolean[] v2 = g.getValue(r2, Bindings.BOOLEAN_ARRAY);
1475                         return Arrays.equals(v1,v2);
1476                 } else if (t1.equals(b.ByteArray)) {
1477                         byte[] v1 = g.getValue(r1, Bindings.BYTE_ARRAY);
1478                         byte[] v2 = g.getValue(r2, Bindings.BYTE_ARRAY);
1479                         return Arrays.equals(v1,v2);
1480                 } else if (t1.equals(b.LongArray)) {
1481                         long[] v1 = g.getValue(r1, Bindings.LONG_ARRAY);
1482                         long[] v2 = g.getValue(r2, Bindings.LONG_ARRAY);
1483                         return Arrays.equals(v1,v2);
1484                 } else {
1485                         Object v1 = g.getValue(r1);
1486                         Object v2 = g.getValue(r2);
1487                         return compareValue(v1, v2);
1488                 }
1489                 
1490         }
1491         
1492         public static boolean compareValue(Object v1, Object v2) {
1493                 if (v1 instanceof Object[] && v2 instanceof Object[])
1494                         return Arrays.deepEquals((Object[])v1, (Object[])v2);
1495                 else if (v1 instanceof int[] && v2 instanceof int[]) 
1496                         return Arrays.equals((int[])v1, (int[])v2);
1497                 else if (v1 instanceof float[] && v2 instanceof float[]) 
1498                         return Arrays.equals((float[])v1, (float[])v2);
1499                 else if (v1 instanceof double[] && v2 instanceof double[]) 
1500                         return Arrays.equals((double[])v1, (double[])v2);
1501                 else if (v1 instanceof long[] && v2 instanceof long[]) 
1502                         return  Arrays.equals((long[])v1, (long[])v2);
1503                 else if (v1 instanceof byte[] && v2 instanceof byte[]) 
1504                         return Arrays.equals((byte[])v1, (byte[])v2);
1505                 else if (v1 instanceof boolean[] && v2 instanceof boolean[]) 
1506                         return Arrays.equals((boolean[])v1, (boolean[])v2);
1507                 else
1508                         return v1.equals(v2);
1509         }
1510
1511         
1512         public class PredicateComparator implements Comparator<Statement> {
1513                 @Override
1514                 public int compare(Statement o1, Statement o2) {
1515                         if (comparableResources.contains(o1.getPredicate(), o2.getPredicate()))
1516                                 return 0;
1517                         if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
1518                                 return -1;
1519                         if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
1520                                 return 1;
1521                         return 0;
1522                 }
1523         }
1524         
1525         public class SubjectComparator implements Comparator<Statement> {
1526                 @Override
1527                 public int compare(Statement o1, Statement o2) {
1528                         if (comparableResources.contains(o1.getSubject(), o2.getSubject()))
1529                                 return 0;
1530                         if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
1531                                 return -1;
1532                         if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
1533                                 return 1;
1534                         return 0;
1535                 }
1536         }
1537         
1538         public class ObjectComparator implements Comparator<Statement> {
1539                 @Override
1540                 public int compare(Statement o1, Statement o2) {
1541                         if (comparableResources.contains(o1.getObject(), o2.getObject()))
1542                                 return 0;
1543                         if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
1544                                 return -1;
1545                         if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
1546                                 return 1;
1547                         return 0;
1548                 }
1549         }
1550         
1551         public static class FullStatementComparator implements Comparator<Statement> {
1552                 @Override
1553                 public int compare(Statement o1, Statement o2) {
1554                         if (o1.getSubject().getResourceId() < o2.getSubject().getResourceId())
1555                                 return -1;
1556                         if (o1.getSubject().getResourceId() > o2.getSubject().getResourceId())
1557                                 return 1;
1558                         if (o1.getPredicate().getResourceId() < o2.getPredicate().getResourceId())
1559                                 return -1;
1560                         if (o1.getPredicate().getResourceId() > o2.getPredicate().getResourceId())
1561                                 return 1;
1562                         if (o1.getObject().getResourceId() < o2.getObject().getResourceId())
1563                                 return -1;
1564                         if (o1.getObject().getResourceId() > o2.getObject().getResourceId())
1565                                 return 1;
1566                         return 0;
1567                 }
1568         }
1569         
1570         public class ResComparator implements Comparator<Resource> {
1571                 @Override
1572                 public int compare(Resource o1, Resource o2) {
1573                         if (comparableResources.contains(o1, o2))
1574                                 return 0;
1575                         if (o1.getResourceId() < o2.getResourceId())
1576                                 return -1;
1577                         if (o1.getResourceId() > o2.getResourceId())
1578                                 return 1;
1579                         return 0;
1580                 }
1581         }
1582
1583 }