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