commit baa52718bdb343980370b95a1185ffccae2681fc
parent f81185240e76a6b1a40a661d4cc925043382c992
Author: Dan Callaghan <djc@djc.id.au>
Date: Sun, 11 Oct 2009 17:37:46 +1000
support multiple comma-separate selectors in sort order
Diffstat:
8 files changed, 156 insertions(+), 60 deletions(-)
diff --git a/src/main/antlr3/au/com/miskinhill/rdftemplate/selector/Selector.g b/src/main/antlr3/au/com/miskinhill/rdftemplate/selector/Selector.g
@@ -114,10 +114,10 @@ traversal returns [Traversal result]
| // optional
)
( '('
- ( '~' { $result.setReverseSorted(true); }
- | // optional
- )
- s=selector { $result.setSortOrder(s.withResultType(Comparable.class)); }
+ so=sortOrder { $result.addSortOrderComparator(so); }
+ ( ','
+ so=sortOrder { $result.addSortOrderComparator(so); }
+ )*
')'
| // optional
)
@@ -128,6 +128,16 @@ traversal returns [Traversal result]
)
;
+sortOrder returns [SelectorComparator<? extends Comparable<?>> result]
+@init {
+ result = new SelectorComparator();
+}
+ : ( '~' { $result.setReversed(true); }
+ | // optional
+ )
+ s=selector { $result.setSelector((Selector) s.withResultType(Comparable.class)); }
+ ;
+
booleanPredicate returns [Predicate result]
: ( p=predicate { result = p; }
| left=predicate
diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorComparator.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorComparator.java
@@ -0,0 +1,46 @@
+/**
+ *
+ */
+package au.com.miskinhill.rdftemplate.selector;
+
+import java.util.Comparator;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import com.hp.hpl.jena.rdf.model.RDFNode;
+
+public class SelectorComparator<T extends Comparable<T>> implements Comparator<RDFNode> {
+
+ private Selector<T> selector;
+ private boolean reversed = false;
+
+ public Selector<T> getSelector() {
+ return selector;
+ }
+
+ public void setSelector(Selector<T> selector) {
+ this.selector = selector;
+ }
+
+ public boolean isReversed() {
+ return reversed;
+ }
+
+ public void setReversed(boolean reversed) {
+ this.reversed = reversed;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append(selector).append("reversed", reversed).toString();
+ }
+
+ @Override
+ public int compare(RDFNode left, RDFNode right) {
+ T leftKey = selector.singleResult(left);
+ T rightKey = selector.singleResult(right);
+ int result = leftKey.compareTo(rightKey);
+ return reversed ? -result : result;
+ }
+
+}
+\ No newline at end of file
diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/Traversal.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/Traversal.java
@@ -22,11 +22,21 @@ public class Traversal {
private String propertyLocalName;
private boolean inverse = false;
private Predicate predicate;
- private Selector<? extends Comparable<?>> sortOrder;
- private Comparator<RDFNode> _sortComparator;
- private boolean reverseSorted = false;
+ private List<Comparator<RDFNode>> sortOrder = new ArrayList<Comparator<RDFNode>>();
private Integer subscript;
+ private class SortComparator implements Comparator<RDFNode> {
+ @Override
+ public int compare(RDFNode left, RDFNode right) {
+ for (Comparator<RDFNode> comparator: sortOrder) {
+ int result = comparator.compare(left, right);
+ if (result != 0)
+ return result;
+ }
+ return 0;
+ }
+ }
+
public List<RDFNode> traverse(RDFNode node) {
if (!node.isResource()) {
throw new SelectorEvaluationException("Attempted to traverse non-resource node " + node);
@@ -46,8 +56,8 @@ public class Traversal {
}
}
CollectionUtils.filter(destinations, predicate);
- if (_sortComparator != null)
- Collections.sort(destinations, reverseSorted ? Collections.reverseOrder(_sortComparator) : _sortComparator);
+ if (!sortOrder.isEmpty())
+ Collections.sort(destinations, new SortComparator());
if (subscript != null) {
if (destinations.size() <= subscript) {
throw new SelectorEvaluationException("Cannot apply subscript " + subscript + " to nodes " + destinations);
@@ -65,7 +75,6 @@ public class Traversal {
.append("inverse", inverse)
.append("predicate", predicate)
.append("sortOrder", sortOrder)
- .append("reverseSorted", reverseSorted)
.append("subscript", subscript)
.toString();
}
@@ -102,34 +111,12 @@ public class Traversal {
this.predicate = predicate;
}
- public Selector<?> getSortOrder() {
+ public List<Comparator<RDFNode>> getSortOrder() {
return sortOrder;
}
- private static final class SelectorComparator<T extends Comparable<T>> implements Comparator<RDFNode> {
- private final Selector<T> selector;
- public SelectorComparator(Selector<T> selector) {
- this.selector = selector;
- }
- @Override
- public int compare(RDFNode left, RDFNode right) {
- T leftKey = selector.singleResult(left);
- T rightKey = selector.singleResult(right);
- return leftKey.compareTo(rightKey);
- }
- }
-
- public <T extends Comparable<T>> void setSortOrder(Selector<T> sortOrder) {
- this.sortOrder = sortOrder;
- this._sortComparator = new SelectorComparator<T>(sortOrder);
- }
-
- public boolean isReverseSorted() {
- return reverseSorted;
- }
-
- public void setReverseSorted(boolean reverseSorted) {
- this.reverseSorted = reverseSorted;
+ public void addSortOrderComparator(Comparator<RDFNode> selector) {
+ this.sortOrder.add(selector);
}
public Integer getSubscript() {
diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorComparatorMatcher.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorComparatorMatcher.java
@@ -0,0 +1,25 @@
+package au.com.miskinhill.rdftemplate.selector;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import org.hamcrest.Matcher;
+
+public class SelectorComparatorMatcher<T extends Comparable<T>> extends BeanPropertyMatcher<SelectorComparator<T>> {
+
+ @SuppressWarnings("unchecked")
+ public SelectorComparatorMatcher() {
+ super((Class<? extends SelectorComparator<T>>) SelectorComparator.class);
+ }
+
+ public static SelectorComparatorMatcher<?> selectorComparator(Matcher<? extends Selector<?>> selector) {
+ SelectorComparatorMatcher<?> m = new SelectorComparatorMatcher();
+ m.addRequiredProperty("selector", selector);
+ return m;
+ }
+
+ public SelectorComparatorMatcher<T> reversed() {
+ addRequiredProperty("reversed", equalTo(true));
+ return this;
+ }
+
+}
diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorEvaluationUnitTest.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorEvaluationUnitTest.java
@@ -1,5 +1,10 @@
package au.com.miskinhill.rdftemplate.selector;
+import static au.com.miskinhill.rdftemplate.selector.AdaptationMatcher.comparableLVAdaptation;
+import static au.com.miskinhill.rdftemplate.selector.SelectorComparatorMatcher.selectorComparator;
+import static au.com.miskinhill.rdftemplate.selector.SelectorMatcher.selector;
+import static au.com.miskinhill.rdftemplate.selector.TraversalMatcher.traversal;
+
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
@@ -161,4 +166,15 @@ public class SelectorEvaluationUnitTest {
assertThat(results, hasItems((RDFNode) article, (RDFNode) citedArticle, (RDFNode) anotherReview));
}
+ @Test
+ public void shouldEvaluateMultipleSortSelectors() throws Exception {
+ List<RDFNode> results = selectorFactory.get("!dc:creator[uri-prefix='http://miskinhill.com.au/journals/']" +
+ "(~dc:isPartOf/mhs:publicationDate#comparable-lv,mhs:startPage#comparable-lv)")
+ .withResultType(RDFNode.class).result(author);
+ assertThat(results.size(), equalTo(3));
+ assertThat(results.get(0), equalTo((RDFNode) obituary));
+ assertThat(results.get(1), equalTo((RDFNode) article));
+ assertThat(results.get(2), equalTo((RDFNode) review));
+ }
+
}
diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorParserUnitTest.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorParserUnitTest.java
@@ -1,12 +1,13 @@
package au.com.miskinhill.rdftemplate.selector;
-import org.junit.Before;
-
import static au.com.miskinhill.rdftemplate.selector.AdaptationMatcher.*;
import static au.com.miskinhill.rdftemplate.selector.PredicateMatcher.*;
import static au.com.miskinhill.rdftemplate.selector.SelectorMatcher.*;
-import static au.com.miskinhill.rdftemplate.selector.TraversalMatcher.traversal;
-import static org.junit.Assert.assertThat;
+import static au.com.miskinhill.rdftemplate.selector.SelectorComparatorMatcher.*;
+import static au.com.miskinhill.rdftemplate.selector.TraversalMatcher.*;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
import com.hp.hpl.jena.rdf.model.RDFNode;
import org.junit.Test;
@@ -47,8 +48,8 @@ public class SelectorParserUnitTest {
Selector<RDFNode> selector = factory.get("!mhs:isIssueOf(mhs:publicationDate#comparable-lv)").withResultType(RDFNode.class);
assertThat(selector, selector(
traversal("mhs", "isIssueOf").inverse()
- .withSortOrder(selector(traversal("mhs", "publicationDate"))
- .withAdaptation(comparableLVAdaptation()))));
+ .withSortOrder(selectorComparator(selector(traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())))));
}
@Test
@@ -56,9 +57,8 @@ public class SelectorParserUnitTest {
Selector<RDFNode> selector = factory.get("!mhs:isIssueOf(~mhs:publicationDate#comparable-lv)").withResultType(RDFNode.class);
assertThat(selector, selector(
traversal("mhs", "isIssueOf").inverse()
- .withSortOrder(selector(traversal("mhs", "publicationDate"))
- .withAdaptation(comparableLVAdaptation()))
- .reverseSorted()));
+ .withSortOrder(selectorComparator(selector(traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())).reversed())));
}
@Test
@@ -66,8 +66,8 @@ public class SelectorParserUnitTest {
Selector<RDFNode> selector = factory.get("!mhs:reviews(dc:isPartOf/mhs:publicationDate#comparable-lv)").withResultType(RDFNode.class);
assertThat(selector, selector(
traversal("mhs", "reviews")
- .withSortOrder(selector(traversal("dc", "isPartOf"), traversal("mhs", "publicationDate"))
- .withAdaptation(comparableLVAdaptation()))));
+ .withSortOrder(selectorComparator(selector(traversal("dc", "isPartOf"), traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())))));
}
@Test
@@ -102,9 +102,8 @@ public class SelectorParserUnitTest {
traversal("mhs", "isIssueOf")
.inverse()
.withPredicate(uriPrefixPredicate("http://miskinhill.com.au/journals/"))
- .withSortOrder(selector(traversal("mhs", "publicationDate"))
- .withAdaptation(comparableLVAdaptation()))
- .reverseSorted()));
+ .withSortOrder(selectorComparator(selector(traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())).reversed())));
}
@Test
@@ -115,9 +114,8 @@ public class SelectorParserUnitTest {
assertThat(selector, selector(
traversal("mhs", "isIssueOf")
.inverse()
- .withSortOrder(selector(traversal("mhs", "publicationDate"))
- .withAdaptation(comparableLVAdaptation()))
- .reverseSorted()
+ .withSortOrder(selectorComparator(selector(traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())).reversed())
.withSubscript(0),
traversal("mhs", "coverThumbnail"))
.withAdaptation(uriAdaptation()));
@@ -157,6 +155,17 @@ public class SelectorParserUnitTest {
selector(traversal("mhs", "translator").inverse())));
}
+ @Test
+ public void shouldRecogniseMultipleSortSelectors() throws Exception {
+ Selector<RDFNode> selector = factory.get("!dc:creator(~dc:isPartOf/mhs:publicationDate#comparable-lv,mhs:startPage#comparable-lv)").withResultType(RDFNode.class);
+ assertThat(selector, selector(
+ traversal("dc", "creator").inverse()
+ .withSortOrder(
+ selectorComparator(selector(traversal("dc", "isPartOf"), traversal("mhs", "publicationDate"))
+ .withAdaptation(comparableLVAdaptation())).reversed(),
+ selectorComparator(selector(traversal("mhs", "startPage")).withAdaptation(comparableLVAdaptation())))));
+ }
+
@Test(expected = InvalidSelectorSyntaxException.class)
public void shouldThrowForInvalidSyntax() throws Exception {
factory.get("dc:creator]["); // this is a parser error
diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/TraversalMatcher.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/TraversalMatcher.java
@@ -1,8 +1,11 @@
package au.com.miskinhill.rdftemplate.selector;
-
import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+
+import java.util.Comparator;
+import com.hp.hpl.jena.rdf.model.RDFNode;
import org.hamcrest.Matcher;
public class TraversalMatcher extends BeanPropertyMatcher<Traversal> {
@@ -28,13 +31,8 @@ public class TraversalMatcher extends BeanPropertyMatcher<Traversal> {
return this;
}
- public TraversalMatcher withSortOrder(Matcher<Selector<?>> sortOrder) {
- addRequiredProperty("sortOrder", sortOrder);
- return this;
- }
-
- public TraversalMatcher reverseSorted() {
- addRequiredProperty("reverseSorted", equalTo(true));
+ public TraversalMatcher withSortOrder(Matcher<? extends Comparator<RDFNode>>... sortOrder) {
+ addRequiredProperty("sortOrder", hasItems(sortOrder));
return this;
}
diff --git a/src/test/resources/au/com/miskinhill/rdftemplate/test-data.xml b/src/test/resources/au/com/miskinhill/rdftemplate/test-data.xml
@@ -96,6 +96,8 @@
<dc:isPartOf rdf:resource="http://miskinhill.com.au/journals/test/1:1/"/>
<mhs:reviews rdf:resource="http://miskinhill.com.au/cited/books/test"/>
<dc:creator rdf:resource="http://miskinhill.com.au/authors/test-author"/>
+ <mhs:startPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">205</mhs:startPage>
+ <mhs:endPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">206</mhs:endPage>
</mhs:Review>
<mhs:Review rdf:about="http://miskinhill.com.au/journals/test/2:1/reviews/another-review">
<dc:isPartOf rdf:resource="http://miskinhill.com.au/journals/test/2:1/"/>
@@ -107,6 +109,8 @@
<dc:isPartOf rdf:resource="http://miskinhill.com.au/journals/test/1:1/"/>
<dc:creator rdf:resource="http://miskinhill.com.au/authors/test-author"/>
<mhs:obituaryOf rdf:nodeID="person"/>
+ <mhs:startPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">1</mhs:startPage>
+ <mhs:endPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">2</mhs:endPage>
</mhs:Obituary>
<foaf:Person rdf:nodeID="person">
<foaf:name>John Doe</foaf:name>