X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=inline;f=org.simantics.interop%2Fsrc%2Forg%2Fsimantics%2Finterop%2Ftest%2FGraphComparator.java;h=6064d49f3c734968098930c8518d3fa31d33297b;hb=a43434a612fa0aafd0d4f17ebed2b770c4b1d585;hp=52a8bab650c401d8288be76e7fbcba93455a84f4;hpb=212801c71a012172929a33ffd9643e107eec4950;p=simantics%2Finterop.git diff --git a/org.simantics.interop/src/org/simantics/interop/test/GraphComparator.java b/org.simantics.interop/src/org/simantics/interop/test/GraphComparator.java index 52a8bab..6064d49 100644 --- a/org.simantics.interop/src/org/simantics/interop/test/GraphComparator.java +++ b/org.simantics.interop/src/org/simantics/interop/test/GraphComparator.java @@ -26,6 +26,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.Stack; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; @@ -85,11 +87,16 @@ public class GraphComparator { private Set nonMatchedLeft = new HashSet(); private Set nonMatchedRight = new HashSet(); + private long iter = 0; + private long maxIter = -1; + // runtime attributes private ReadGraph g; private Layer0 b; + private boolean mapLiterals = true; + public GraphComparator(Resource r1, Resource r2) { this.r1 = r1; this.r2 = r2; @@ -102,9 +109,6 @@ public class GraphComparator { this.comparator = comparator; } - ArrayList ss1 = new ArrayList(); - ArrayList ss2 = new ArrayList(); - public Comparator getResourceComparator() { return rcomp; @@ -184,12 +188,74 @@ public class GraphComparator { nonMatchedRight.add(r); } + public long getMaxIter() { + return maxIter; + } + + /** + * Sets maximum iteration done on a single DB transaction. Affects only test(Session) methods. + * Default value is -1, which disables iteration limit. + * Note that using iteration limit causes comparison to repeat some work, thus total execution time will increase. + * + * @param maxIter + */ + public void setMaxIter(long maxIter) { + this.maxIter = maxIter; + } + + public boolean isMapLiterals() { + return mapLiterals; + } + + public void setMapLiterals(boolean mapLiterals) { + this.mapLiterals = mapLiterals; + } + + private void clear() { + changes1.clear(); + changes1Set.clear(); + changes2.clear(); + changes2Set.clear(); + comparableResources.clear(); + comparableStatements.clear(); + modifications.clear(); + modificationsSet.clear(); + processedResources.clear(); + } + + public void dispose() { + changes1 = null; + changes1Set = null; + changes2 = null; + changes2Set = null; + comparableResources = null; + comparableStatements = null; + comparator = null; + modifications = null; + modificationsSet = null; + processedResources = null; + nonMatchedLeft = null; + nonMatchedRight = null; + nonTested = null; + nonTraversed = null; + strong = null; + tested = null; + traversed = null; + } + public void test(ReadGraph g) throws DatabaseException { + test(g,null); + } + + public void test(ReadGraph g, IProgressMonitor monitor) throws DatabaseException { this.g = g; this.b = Layer0.getInstance(g); comparator.setComparator(this); comparator.initialize(g, r1, r2); + if (monitor == null) + monitor = new NullProgressMonitor(); + Stack objectsLeft = new Stack(); Stack objectsRight = new Stack(); objectsLeft.push(r1); @@ -202,22 +268,16 @@ public class GraphComparator { while (true) { if (objectsLeft.isEmpty() && !changed) break; + if (monitor.isCanceled()) { + clear(); + return; + } + monitor.subTask(monitorTaskName(objectsLeft, objectsRight, unreliableLeft, unreliableRight)); changed = false; // process compares objects that are identified and searches for more resources to process. - changed |= process(objectsLeft, objectsRight, unreliableLeft, unreliableRight); - // process unreliable handles cases where unidentified statements subject and object have been identified - changed |= processUnreliable(unreliableLeft, unreliableRight); - // process unreliable handles cases where unidentified resources have path of length one to identified resource - changed |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight); - if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { - changed |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight); - } - if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { - // comparison is ending, but we have still unprocessed unidentified resources left. - // These cases have longer path than one to identified objects. - changed |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight); - } + changed = compareIter(g, objectsLeft, objectsRight, unreliableLeft, unreliableRight); + monitor.worked(1); } for (Statement s : unreliableLeft) { @@ -232,16 +292,29 @@ public class GraphComparator { addAddition(s); } } + monitor.subTask(monitorTaskName(objectsLeft, objectsRight, unreliableLeft, unreliableRight) + " done."); + //printStats(); } public void test(Session session) throws DatabaseException { - test(session, r1, r2); + test(session, r1, r2, null); + } + + public void test(Session session, IProgressMonitor monitor) throws DatabaseException { + test(session, r1, r2, monitor); } public void test(Session session, Resource r1, Resource r2) throws DatabaseException { + test(session, r1, r2, null); + } + + public void test(Session session, Resource r1, Resource r2, IProgressMonitor monitor) throws DatabaseException { comparator.setComparator(this); + if (monitor == null) + monitor = new NullProgressMonitor(); + session.syncRequest(new ReadRequest() { @Override @@ -264,35 +337,22 @@ public class GraphComparator { while (true) { if (objectsLeft.isEmpty() && !changed) break; + if (monitor.isCanceled()) { + clear(); + return; + } + monitor.subTask(monitorTaskName(objectsLeft, objectsRight, unreliableLeft, unreliableRight)); changed = session.syncRequest(new Read() { @Override public Boolean perform(ReadGraph graph) throws DatabaseException { + //System.out.println("Iter " + monitorTaskName(objectsLeft, objectsRight, unreliableLeft, unreliableRight)); g = graph; b = Layer0.getInstance(graph); - // process compares objects that are identified and searches for more resources to process. - boolean c = process(objectsLeft, objectsRight, unreliableLeft, unreliableRight); - // process unreliable handles cases where unidentified statements subject and object have been identified - c |= processUnreliable(unreliableLeft, unreliableRight); - // process unreliable handles cases where unidentified resources have path of length one to identified resource - c |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight); - if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { - c |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight); - } - if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { - // comparison is ending, but we have still unprocessed unidentified resources left. - // These cases have longer path than one to identified objects. - c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight); - } - if (objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { - // comparison is ending, but we have still unprocessed unidentified resources left. - // These cases have longer path than one to identified objects. - c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight); - } - return c; + return compareIter(graph, objectsLeft, objectsRight, unreliableLeft, unreliableRight); } }); - + monitor.worked(1); } @@ -304,15 +364,61 @@ public class GraphComparator { if (!comparableStatements.containsRight(s)) addAddition(s); } + unreliableLeft.clear(); + unreliableRight.clear(); - + monitor.subTask(monitorTaskName(objectsLeft, objectsRight, unreliableLeft, unreliableRight) + " done."); + //printStats(); } - private boolean process(Stack objectsLeft, Stack objectsRight, Set unreliableLeft, Set unreliableRight) throws DatabaseException { + private boolean compareIter(ReadGraph graph, Stack objectsLeft, Stack objectsRight, Set unreliableLeft, Set unreliableRight) throws DatabaseException { + // process compares objects that are identified and searches for more resources to process. + iter = 0; + boolean c = process(objectsLeft, objectsRight, unreliableLeft, unreliableRight,false); + if (objectsLeft.isEmpty()) { + // process unreliable handles cases where unidentified statements subject and object have been identified + c |= processUnreliable(unreliableLeft, unreliableRight); + // process unreliable handles cases where unidentified resources have path of length one to identified resource + if (!c) { + c |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight,false); + if (!c && objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { + c |= processUnreliable(unreliableLeft, unreliableRight,objectsLeft,objectsRight,true); + } + if (!c && objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { + c |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight,false); + } + if (!c && objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { + c |= processUnreliable2(unreliableLeft, unreliableRight,objectsLeft,objectsRight,true); + } + if (!c && objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { + // comparison is ending, but we have still unprocessed unidentified resources left. + // These cases have longer path than one to identified objects. + c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight); + } + if (!c && objectsLeft.isEmpty() && unreliableLeft.size() > 0 && unreliableRight.size() > 0) { + // comparison is ending, but we have still unprocessed unidentified resources left. + // These cases have longer path than one to identified objects. + c |= processUnreliableDeep(unreliableLeft, unreliableRight, objectsLeft, objectsRight); + } + } + } + if (!c) + process(objectsLeft, objectsRight, unreliableLeft, unreliableRight,true); + return c; + + } + + private String monitorTaskName(Stack objectsLeft, Stack objectsRight, Set unreliableLeft, Set unreliableRight) { + int cr = comparableResources.size(); + int ch = Math.max(changes1.size(), changes2.size()); + return "Graph compare " + (cr + ch) + " / " + (cr+ch+(Math.max(objectsLeft.size(), objectsRight.size())+Math.max(unreliableLeft.size(), unreliableRight.size()))); + } + + private boolean process(Stack objectsLeft, Stack objectsRight, Set unreliableLeft, Set unreliableRight, boolean useBest) throws DatabaseException { List ss1 = new ArrayList(); List ss2 = new ArrayList(); boolean didSomething = false; - while (!objectsLeft.isEmpty()) { + while (!objectsLeft.isEmpty()&& (maxIter < 0 || iter < maxIter)) { Resource r1 = objectsLeft.pop(); Resource r2 = objectsRight.pop(); @@ -343,7 +449,7 @@ public class GraphComparator { ss1 = filter(Collections.singletonList(b.HasProperty), ss1); ss2 = filter(Collections.singletonList(b.HasProperty), ss2); - compareStatements(ss1, ss2, null, null,null,null); + compareStatements(ss1, ss2, null, null,null,null, useBest); ss1.clear(); ss2.clear(); } @@ -355,11 +461,12 @@ public class GraphComparator { ss2 = filterAsserted(r2, ss2); ss1 = filterNonTraversed(ss1); ss2 = filterNonTraversed(ss2); - compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight); + compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight, useBest); ss1.clear(); ss2.clear(); } + iter++; } return didSomething; } @@ -380,6 +487,8 @@ public class GraphComparator { } for (Resource left : subjectLeft.getKeys()) { + if (maxIter > 0 && iter > maxIter) + break; Resource right = comparableResources.getRight(left); if (right == null) continue; @@ -401,6 +510,7 @@ public class GraphComparator { unreliableRight.remove(rightS); addComparable(leftS, rightS); didSomething = true; + iter++; } } } @@ -408,7 +518,7 @@ public class GraphComparator { return didSomething; } - private boolean processUnreliable(Set unreliableLeft, Set unreliableRight, Stack objectsLeft, Stack objectsRight) throws DatabaseException { + private boolean processUnreliable(Set unreliableLeft, Set unreliableRight, Stack objectsLeft, Stack objectsRight, boolean useBest) throws DatabaseException { MapList subjectLeft = new MapList(); MapList subjectRight = new MapList(); MapList objectLeft = new MapList(); @@ -422,8 +532,9 @@ public class GraphComparator { subjectRight.add(s.getSubject(),s); objectRight.add(s.getObject(),s); } - for (Resource ol : objectLeft.getKeys()) { + if (maxIter > 0 && iter > maxIter) + break; // all statements to the left side object List left = objectLeft.getValues(ol); // all subjects that have statements to the left side object (ol) @@ -447,8 +558,6 @@ public class GraphComparator { } } - compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight); - for (Resource similarOl : comparableOLeft.getKeys()) { List similarLeft = comparableOLeft.getValues(similarOl); if (similarLeft.size() == left.size()) { @@ -581,6 +690,7 @@ public class GraphComparator { unreliableLeft.remove(sl); unreliableRight.remove(sr); } + iter++; } else { @@ -592,7 +702,7 @@ public class GraphComparator { } - private boolean processUnreliable2(Set unreliableLeft, Set unreliableRight, Stack objectsLeft, Stack objectsRight) throws DatabaseException { + private boolean processUnreliable2(Set unreliableLeft, Set unreliableRight, Stack objectsLeft, Stack objectsRight, boolean useBest) throws DatabaseException { MapList subjectLeft = new MapList(); MapList subjectRight = new MapList(); MapList objectLeft = new MapList(); @@ -606,8 +716,10 @@ public class GraphComparator { subjectRight.add(s.getSubject(),s); objectRight.add(s.getObject(),s); } - + Set> processed = new HashSet>(); for (Resource ol : objectLeft.getKeys()) { + if (maxIter > 0 && iter > maxIter) + break; // all statements to the left side object List left = objectLeft.getValues(ol); // all subjects that have statements to the left side object (ol) @@ -620,16 +732,48 @@ public class GraphComparator { } if (sLeft.size() == 1 && sRight.size() == 1) { - List ss1 = new ArrayList(subjectLeft.getValues(sLeft.iterator().next())); - List ss2 = new ArrayList(subjectRight.getValues(sRight.iterator().next())); + Resource sl = sLeft.iterator().next(); + Resource sr = sRight.iterator().next(); + Pair p = new Pair(sl, sr); + if (processed.contains(p)) + continue; + processed.add(p); + List ss1 = new ArrayList(subjectLeft.getValuesSnapshot(sl)); + List ss2 = new ArrayList(subjectRight.getValuesSnapshot(sr)); + for (int i = ss1.size() -1; i >= 0; i--) { + if (!unreliableLeft.contains(ss1.get(i))) + ss1.remove(i); + } + for (int i = ss2.size() -1; i >= 0; i--) { + if (!unreliableRight.contains(ss2.get(i))) + ss2.remove(i); + } + + int ccount = comparableStatements.size(); + int lcount = changes1.size(); + int rcount = changes2.size(); - int count = comparableStatements.size(); - compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight); - if (comparableStatements.size() > count) { + compareStatements(ss1, ss2, objectsLeft, objectsRight,unreliableLeft,unreliableRight, useBest); + if (comparableStatements.size() > ccount) { didSomething = true; for (Entry entry : comparableStatements.getEntries()) { unreliableLeft.remove(entry.getKey()); unreliableRight.remove(entry.getValue()); + iter++; + } + } + if (changes1.size() > lcount) { + didSomething = true; + for (Statement stm : changes1) { + unreliableLeft.remove(stm); + iter++; + } + } + if (changes2.size() > rcount) { + didSomething = true; + for (Statement stm : changes2) { + unreliableRight.remove(stm); + iter++; } } } @@ -652,6 +796,8 @@ public class GraphComparator { objectRight.add(s.getObject(),s); } for (Resource ol : objectLeft.getKeys()) { + if (maxIter > 0 && iter > maxIter) + break; Set pathsLeft = new HashSet(); for (Resource rel : traversed) { pathsLeft.addAll(Path.create(g.getStatements(ol, rel))); @@ -691,23 +837,62 @@ public class GraphComparator { } if (matchingPaths.size() > 0) { if (matchingPaths.size() == 1) { - didSomething = true; Resource or = matchingPaths.keySet().iterator().next(); - - objectsLeft.add(ol); - objectsRight.add(or); - addComparable(ol, or); - Collection statementsLeft = objectLeft.getValues(ol); - Collection statementsRight = objectRight.getValues(or); - unreliableLeft.removeAll(statementsLeft); - unreliableRight.removeAll(statementsRight); BijectionMap map = getMatchingPaths(pathsLeft, matchingPaths.get(or)); + BijectionMap toMap = new BijectionMap(); + toMap.map(ol, or); + boolean pass = true; + // Path matching may create conflicting results, prevent selecting them. for (Path left : map.getLeftSet()) { Path right = map.getRight(left); for (int i = 0; i < left.getLength(); i++) { - addComparable(left.getStatements().get(i),right.getStatements().get(i)); + Resource l = left.getStatements().get(i).getObject(); + Resource r = right.getStatements().get(i).getObject(); + if (comparableResources.contains(l, r)) + continue; + if (comparableResources.containsLeft(l)) { + pass = false; + break; + } + if (comparableResources.containsRight(r)) { + pass = false; + break; + } + if (toMap.contains(l,r)) + continue; + if (toMap.containsLeft(l)) { + pass = false; + break; + } + if (toMap.containsRight(r)) { + pass = false; + break; + } + toMap.map(l, r); + } } + if (pass) { + didSomething = true; + + objectsLeft.add(ol); + objectsRight.add(or); + addComparable(ol, or); + Collection statementsLeft = objectLeft.getValues(ol); + Collection statementsRight = objectRight.getValues(or); + unreliableLeft.removeAll(statementsLeft); + unreliableRight.removeAll(statementsRight); + for (Path left : map.getLeftSet()) { + Path right = map.getRight(left); + for (int i = 0; i < left.getLength(); i++) { + addComparable(left.getStatements().get(i),right.getStatements().get(i)); + iter++; + } + } + iter++; + } else { + iter++; + } } } } @@ -836,11 +1021,18 @@ public class GraphComparator { } else { if (DEBUG) System.out.println(left + " = " + right); comparableResources.map(left, right); + +// if (comparableResources.size() % 1000 == 0) +// printStats(); } } } + public void printStats() { + System.out.println("Comp " + comparableResources.size() + " L " + changes1.size() + " R " + changes2.size() + " M " + modifications.size() + " P " + processedResources.size()); + } + public List filterAsserted(Resource r, Collection in) throws DatabaseException { List out = new ArrayList(); for (Statement s : in) { @@ -926,11 +1118,13 @@ public class GraphComparator { } } - private void addModification(Resource left, Statement leftstm, Resource right, Statement rightstm) { + private void addModification(Resource left, Statement leftstm, Resource right, Statement rightstm) throws DatabaseException { Modification mod = new Modification(left, right, leftstm, rightstm); if (!modificationsSet.contains(mod)) { modificationsSet.add(mod); modifications.add(mod); + if (mapLiterals && leftstm != null && rightstm != null) + addComparable(leftstm, rightstm); } } @@ -1012,7 +1206,7 @@ public class GraphComparator { } } - private void compareStatements(List ss1, List ss2, Stack objectsLeft, Stack objectsRight, Collection unreliableLeft, Collection unreliableRight) throws DatabaseException { + private void compareStatements(List ss1, List ss2, Stack objectsLeft, Stack objectsRight, Collection unreliableLeft, Collection unreliableRight, boolean useBest) throws DatabaseException { sortStatement(ss1, ss2); int i1 = 0; @@ -1043,7 +1237,7 @@ public class GraphComparator { int same2 = sameRel(ss2, i2); int c = rcomp.compare(ss1.get(i1).getPredicate(),ss2.get(i2).getPredicate()); if (c == 0) { - compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight); + compareStatements(ss1, i1, same1, ss2, i2, same2,objectsLeft,objectsRight,unreliableLeft,unreliableRight, useBest); i1+=same1; i2+=same2; } else if (c < 0) { @@ -1096,7 +1290,7 @@ public class GraphComparator { return comparator.compare(g, o1, o2); } - private void compareStatements(List ss1, int off1, int len1, List ss2, int off2, int len2, Collection objectsLeft, Collection objectsRight, Collection unreliableLeft, Collection unreliableRight) throws DatabaseException { + private void compareStatements(List ss1, int off1, int len1, List ss2, int off2, int len2, Collection objectsLeft, Collection objectsRight, Collection unreliableLeft, Collection unreliableRight, boolean useBest) throws DatabaseException { boolean[] used1 = new boolean[len1]; for (int i = 0; i < used1.length; i++) { used1[i] = false; @@ -1139,10 +1333,13 @@ public class GraphComparator { Integer[] pris = priorities.getKeys(new Integer[]{}); Arrays.sort(pris); boolean matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, false); - if (matchFail) - matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight, objectsLeft == null); + if (matchFail && objectsLeft == null) + matchFail = priorityMatching(ss1, off1, len1, ss2, off2, len2, pris, differences, priorities, used1, used2, objectsLeft, objectsRight,true); + if (unreliableLeft != null) { if (matchFail) { + if (useBest) + bestMatching(ss1, off1, len1, ss2, off2, len2, differences, priorities, used1, used2, objectsLeft, objectsRight); // With match failure, statement matching was aborted. We must move all unmatched statements to unreliable. for (Integer pri : pris) { if (pri == 0 || pri == Integer.MAX_VALUE) @@ -1292,6 +1489,63 @@ public class GraphComparator { } return matchFail; } + + /** + * priority matching has a flaw, if lowest priority produces conflicting results, it is not able to match anything. + * This matching logic looks for a best match, and if it is non conflicting applies the match. + * + * TODO: now this applies all unique lowest priority matches for each object. Should this pick only the object with global lowest priority, + * so that new matches improve situation with conflicting (non unique) matches? + * + * @param ss1 + * @param off1 + * @param len1 + * @param ss2 + * @param off2 + * @param len2 + * @param differences + * @param priorities + * @param used1 + * @param used2 + * @param objectsLeft + * @param objectsRight + * @throws DatabaseException + */ + private void bestMatching(List ss1, int off1, int len1, List ss2, int off2, int len2, List> differences, MapList priorities, boolean used1[],boolean used2[], Collection objectsLeft, Collection objectsRight) throws DatabaseException { + for (int i1 = 0; i1 < differences.size(); i1++) { + if (used1[i1]) + continue; + List diffs = differences.get(i1); + int min = Integer.MAX_VALUE; + int minIndex = -1; + for (int j = 0; j < diffs.size(); j++) { + int p = diffs.get(j); + if (p < 0) + continue; + if (p < min) { + min = p; + minIndex = j; + } else if (p == min) { + minIndex = -1; + } + } + if (minIndex >= 0 && min > 0) { + int i2 = minIndex; + if (!used2[i2]) { + used1[i1] = true; + used2[i2] = true; + Statement s1 = ss1.get(i1+off1); + Statement s2 = ss2.get(i2+off2); + + if (objectsLeft != null) { + objectsLeft.add(s1.getObject()); + objectsRight.add(s2.getObject()); + } + addComparable(s1, s2); + } + } + } + } /** @@ -1360,9 +1614,9 @@ public class GraphComparator { boolean eq = compareValue(g,b,s1.getObject(), s2.getObject()); if (!eq) { addModification(r1,s1,r2,s2); - if (!a1 && !a2) - addComparable(s1, s2); } + if (mapLiterals && !a1 && !a2) + addComparable(s1, s2); } else { // Non literal properties. int comp = comparator.compare(g, s1.getObject(), s2.getObject());