commit 4f25607ea77ca8eb8953b99705c85de97ad9d984 parent b68f599d66065f4a9997216495960fb69629b543 Author: Dan Callaghan <djc@djc.id.au> Date: Sun, 4 Oct 2009 16:22:01 +1000 added support for: type predicate, boolean combination of predicates, unions Diffstat:
12 files changed, 347 insertions(+), 43 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 @@ -11,7 +11,7 @@ package au.com.miskinhill.rdftemplate.selector; CommonTokenStream tokens = new CommonTokenStream(lexer); SelectorParser parser = new SelectorParser(tokens); try { - return parser.selector(); + return parser.unionSelector(); } catch (RecognitionException e) { throw new InvalidSelectorSyntaxException(e); } @@ -34,10 +34,27 @@ package au.com.miskinhill.rdftemplate.selector; } } -start : selector ; +start : unionSelector ; + +unionSelector returns [Selector<?> result] +@init { + List<Selector<?>> selectors = new ArrayList<Selector<?>>(); +} + : s=selector { selectors.add(s); } + ( '|' + s=selector { selectors.add(s); } + )* + { + if (selectors.size() > 1) + result = new UnionSelector(selectors); + else + result = selectors.get(0); + } + ; selector returns [Selector<?> result] - : ( ts=traversingSelector { result = ts; } + : ' '* + ( ts=traversingSelector { result = ts; } | { result = new NoopSelector(); } ) ( '#' @@ -52,6 +69,7 @@ selector returns [Selector<?> result] ) | ) + ' '* ; traversingSelector returns [TraversingSelector result] @@ -75,9 +93,7 @@ traversal returns [Traversal result] ':' localname=XMLTOKEN { $result.setPropertyLocalName($localname.text); } ( '[' - URI_PREFIX_PREDICATE - '=' - uriPrefix=SINGLE_QUOTED { $result.setPredicate(new UriPrefixPredicate($uriPrefix.text)); } + p=booleanPredicate { $result.setPredicate(p); } ']' | // optional ) @@ -96,7 +112,32 @@ traversal returns [Traversal result] ) ; +booleanPredicate returns [Predicate result] + : ( p=predicate { result = p; } + | left=predicate + ' '+ + 'and' + ' '+ + right=booleanPredicate + { result = new BooleanAndPredicate(left, right); } + ) + ; + +predicate returns [Predicate result] + : ( URI_PREFIX_PREDICATE + '=' + uriPrefix=SINGLE_QUOTED { result = new UriPrefixPredicate($uriPrefix.text); } + | TYPE_PREDICATE + '=' + nsprefix=XMLTOKEN + ':' + localname=XMLTOKEN + { result = new TypePredicate($nsprefix.text, $localname.text); } + ) + ; + URI_PREFIX_PREDICATE : 'uri-prefix' ; +TYPE_PREDICATE : 'type' ; FIRST_PREDICATE : 'first' ; LV_ADAPTATION : 'lv' ; COMPARABLE_LV_ADAPTATION : 'comparable-lv' ; diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java @@ -43,6 +43,8 @@ public class TemplateInterpolator { private static final QName CONTENT_ACTION_QNAME = new QName(NS, CONTENT_ACTION); public static final String FOR_ACTION = "for"; private static final QName FOR_ACTION_QNAME = new QName(NS, FOR_ACTION); + public static final String IF_ACTION = "if"; + private static final QName IF_ACTION_QNAME = new QName(NS, IF_ACTION); private static final QName XML_LANG_QNAME = new QName(XMLConstants.XML_NS_URI, "lang", XMLConstants.XML_NS_PREFIX); private static final String XHTML_NS_URI = "http://www.w3.org/1999/xhtml"; @@ -70,25 +72,51 @@ public class TemplateInterpolator { switch (event.getEventType()) { case XMLStreamConstants.START_ELEMENT: { StartElement start = (StartElement) event; - Attribute contentAttribute = start.getAttributeByName(CONTENT_ACTION_QNAME); - Attribute forAttribute = start.getAttributeByName(FOR_ACTION_QNAME); - if (contentAttribute != null && forAttribute != null) { - throw new TemplateSyntaxException("rdf:for and rdf:content cannot both be present on an element"); - } else if (contentAttribute != null) { - consumeTree(start, reader); - start = interpolateAttributes(start, node); - Selector<?> selector = SelectorParser.parse(contentAttribute.getValue()); - writeTreeForContent(writer, start, selector.singleResult(node)); - } else if (forAttribute != null) { - start = cloneStartWithAttributes(start, cloneAttributesWithout(start, FOR_ACTION_QNAME)); - List<XMLEvent> tree = consumeTree(start, reader); - Selector<RDFNode> selector = SelectorParser.parse(forAttribute.getValue()).withResultType(RDFNode.class); - for (RDFNode subNode : selector.result(node)) { - interpolate(tree.iterator(), subNode, writer); + if (start.getName().equals(IF_ACTION_QNAME)) { + Attribute testAttribute = start.getAttributeByName(new QName("test")); + if (testAttribute == null) + throw new TemplateSyntaxException("rdf:if must have a test attribute"); + Selector<?> selector = SelectorParser.parse(testAttribute.getValue()); + if (selector.result(node).isEmpty()) { + consumeTree(start, reader); + break; + } else { + List<XMLEvent> events = consumeTree(start, reader); + // discard the enclosing rdf:if element + events.remove(events.size() - 1); + events.remove(0); + interpolate(events.iterator(), node, writer); } } else { - start = interpolateAttributes(start, node); - writer.add(start); + Attribute ifAttribute = start.getAttributeByName(IF_ACTION_QNAME); + Attribute contentAttribute = start.getAttributeByName(CONTENT_ACTION_QNAME); + Attribute forAttribute = start.getAttributeByName(FOR_ACTION_QNAME); + if (ifAttribute != null) { + Selector<?> selector = SelectorParser.parse(ifAttribute.getValue()); + if (selector.result(node).isEmpty()) { + consumeTree(start, reader); + break; + } + start = cloneStartWithAttributes(start, cloneAttributesWithout(start, IF_ACTION_QNAME)); + } + if (contentAttribute != null && forAttribute != null) { + throw new TemplateSyntaxException("rdf:for and rdf:content cannot both be present on an element"); + } else if (contentAttribute != null) { + consumeTree(start, reader); + start = interpolateAttributes(start, node); + Selector<?> selector = SelectorParser.parse(contentAttribute.getValue()); + writeTreeForContent(writer, start, selector.singleResult(node)); + } else if (forAttribute != null) { + start = cloneStartWithAttributes(start, cloneAttributesWithout(start, FOR_ACTION_QNAME)); + List<XMLEvent> tree = consumeTree(start, reader); + Selector<RDFNode> selector = SelectorParser.parse(forAttribute.getValue()).withResultType(RDFNode.class); + for (RDFNode subNode : selector.result(node)) { + interpolate(tree.iterator(), subNode, writer); + } + } else { + start = interpolateAttributes(start, node); + writer.add(start); + } } break; } diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/BooleanAndPredicate.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/BooleanAndPredicate.java @@ -0,0 +1,34 @@ +package au.com.miskinhill.rdftemplate.selector; + +import com.hp.hpl.jena.rdf.model.RDFNode; +import org.apache.commons.lang.builder.ToStringBuilder; + +public class BooleanAndPredicate implements Predicate { + + private final Predicate left; + private final Predicate right; + + public BooleanAndPredicate(Predicate left, Predicate right) { + this.left = left; + this.right = right; + } + + public Predicate getLeft() { + return left; + } + + public Predicate getRight() { + return right; + } + + @Override + public boolean evaluate(RDFNode node) { + return left.evaluate(node) && right.evaluate(node); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append(left).append(right).toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/TypePredicate.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/TypePredicate.java @@ -0,0 +1,52 @@ +package au.com.miskinhill.rdftemplate.selector; + +import java.util.Set; + +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.Statement; +import com.hp.hpl.jena.vocabulary.RDF; +import org.apache.commons.lang.builder.ToStringBuilder; + +import au.com.miskinhill.rdftemplate.NamespacePrefixMapper; + +public class TypePredicate implements Predicate { + + private final String namespacePrefix; + private final String localName; + + public TypePredicate(String namespacePrefix, String localName) { + this.namespacePrefix = namespacePrefix; + this.localName = localName; + } + + public String getNamespacePrefix() { + return namespacePrefix; + } + + public String getLocalName() { + return localName; + } + + @SuppressWarnings("unchecked") + @Override + public boolean evaluate(RDFNode node) { + if (!node.isResource()) { + throw new SelectorEvaluationException("Attempted to apply [type] to non-resource node " + node); + } + Resource resource = (Resource) node; + Resource type = resource.getModel().createResource( + NamespacePrefixMapper.getInstance().get(namespacePrefix) + localName); + for (Statement statement: (Set<Statement>) resource.listProperties(RDF.type).toSet()) { + if (statement.getObject().equals(type)) + return true; + } + return false; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append(namespacePrefix).append(localName).toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/UnionSelector.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/UnionSelector.java @@ -0,0 +1,45 @@ +package au.com.miskinhill.rdftemplate.selector; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; + +import com.hp.hpl.jena.rdf.model.RDFNode; +import org.apache.commons.lang.builder.ToStringBuilder; + +public class UnionSelector<T> extends AbstractSelector<T> { + + private final List<Selector<? extends T>> selectors; + + public UnionSelector(List<Selector<? extends T>> selectors) { + super(null); + this.selectors = selectors; + } + + @Override + public List<T> result(RDFNode node) { + LinkedHashSet<T> results = new LinkedHashSet<T>(); + for (Selector<? extends T> selector: selectors) { + results.addAll(selector.result(node)); + } + return new ArrayList<T>(results); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append(selectors).toString(); + } + + public List<Selector<? extends T>> getSelectors() { + return selectors; + } + + @Override + public <Other> Selector<Other> withResultType(Class<Other> otherType) { + for (Selector<? extends T> selector: selectors) { + selector.withResultType(otherType); // class cast exception? + } + return (Selector<Other>) this; + } + +} diff --git a/src/test/java/au/com/miskinhill/rdftemplate/TemplateInterpolatorUnitTest.java b/src/test/java/au/com/miskinhill/rdftemplate/TemplateInterpolatorUnitTest.java @@ -59,6 +59,22 @@ public class TemplateInterpolatorUnitTest { } @Test + public void shouldHandleIfs() throws Exception { + Resource author = model.getResource("http://miskinhill.com.au/authors/test-author"); + String result = TemplateInterpolator.interpolate( + new InputStreamReader(this.getClass().getResourceAsStream("conditional.xml")), author); + assertThat(result, containsString("attribute test")); + assertThat(result, containsString("element test")); + assertThat(result, not(containsString("rdf:if"))); + + Resource authorWithoutNotes = model.getResource("http://miskinhill.com.au/authors/another-author"); + result = TemplateInterpolator.interpolate( + new InputStreamReader(this.getClass().getResourceAsStream("conditional.xml")), authorWithoutNotes); + assertThat(result, not(containsString("attribute test"))); + assertThat(result, not(containsString("element test"))); + } + + @Test public void shouldWork() throws Exception { Resource journal = model.getResource("http://miskinhill.com.au/journals/test/"); String result = TemplateInterpolator.interpolate( diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/PredicateMatcher.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/PredicateMatcher.java @@ -2,6 +2,8 @@ package au.com.miskinhill.rdftemplate.selector; import static org.hamcrest.CoreMatchers.equalTo; +import org.hamcrest.Matcher; + public class PredicateMatcher<T extends Predicate> extends BeanPropertyMatcher<T> { private PredicateMatcher(Class<T> type) { @@ -13,5 +15,20 @@ public class PredicateMatcher<T extends Predicate> extends BeanPropertyMatcher<T m.addRequiredProperty("prefix", equalTo(prefix)); return m; } + + public static PredicateMatcher<TypePredicate> typePredicate(String namespacePrefix, String localName) { + PredicateMatcher<TypePredicate> m = new PredicateMatcher<TypePredicate>(TypePredicate.class); + m.addRequiredProperty("namespacePrefix", equalTo(namespacePrefix)); + m.addRequiredProperty("localName", equalTo(localName)); + return m; + } + + public static PredicateMatcher<BooleanAndPredicate> booleanAndPredicate( + Matcher<? extends Predicate> left, Matcher<? extends Predicate> right) { + PredicateMatcher<BooleanAndPredicate> m = new PredicateMatcher<BooleanAndPredicate>(BooleanAndPredicate.class); + m.addRequiredProperty("left", left); + m.addRequiredProperty("right", right); + return m; + } } 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,9 +1,8 @@ package au.com.miskinhill.rdftemplate.selector; -import static org.junit.matchers.JUnitMatchers.hasItems; - import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.hasItems; import java.io.InputStream; import java.util.List; @@ -22,7 +21,7 @@ import au.com.miskinhill.rdftemplate.datatype.DateDataType; public class SelectorEvaluationUnitTest { private Model m; - private Resource journal, issue, article, author, book, review, anotherReview, obituary, en, ru; + private Resource journal, issue, article, citedArticle, author, anotherAuthor, book, review, anotherReview, obituary, en, ru; @BeforeClass public static void ensureDatatypesRegistered() { @@ -37,7 +36,9 @@ public class SelectorEvaluationUnitTest { journal = m.createResource("http://miskinhill.com.au/journals/test/"); issue = m.createResource("http://miskinhill.com.au/journals/test/1:1/"); article = m.createResource("http://miskinhill.com.au/journals/test/1:1/article"); + citedArticle = m.createResource("http://miskinhill.com.au/cited/journals/asdf/1:1/article"); author = m.createResource("http://miskinhill.com.au/authors/test-author"); + anotherAuthor = m.createResource("http://miskinhill.com.au/authors/another-author"); book = m.createResource("http://miskinhill.com.au/cited/books/test"); review = m.createResource("http://miskinhill.com.au/journals/test/1:1/reviews/review"); anotherReview = m.createResource("http://miskinhill.com.au/journals/test/2:1/reviews/another-review"); @@ -134,4 +135,28 @@ public class SelectorEvaluationUnitTest { assertThat(results, hasItems((Object) "en", (Object) "ru")); } + @Test + public void shouldEvaluateTypePredicate() throws Exception { + List<RDFNode> results = SelectorParser.parse("!dc:creator[type=mhs:Review]") + .withResultType(RDFNode.class).result(author); + assertThat(results.size(), equalTo(1)); + assertThat(results, hasItems((RDFNode) review)); + } + + @Test + public void shouldEvaluateAndCombinationOfPredicates() throws Exception { + List<RDFNode> results = SelectorParser.parse("!dc:creator[type=mhs:Article and uri-prefix='http://miskinhill.com.au/journals/']") + .withResultType(RDFNode.class).result(author); + assertThat(results.size(), equalTo(1)); + assertThat(results, hasItems((RDFNode) article)); + } + + @Test + public void shouldEvaluateUnion() throws Exception { + List<RDFNode> results = SelectorParser.parse("!dc:creator | !mhs:translator") + .withResultType(RDFNode.class).result(anotherAuthor); + assertThat(results.size(), equalTo(3)); + assertThat(results, hasItems((RDFNode) article, (RDFNode) citedArticle, (RDFNode) anotherReview)); + } + } diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorMatcher.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorMatcher.java @@ -1,8 +1,8 @@ package au.com.miskinhill.rdftemplate.selector; - import static org.junit.matchers.JUnitMatchers.hasItems; +import com.hp.hpl.jena.rdf.model.RDFNode; import org.hamcrest.Matcher; public class SelectorMatcher<T extends Selector<?>> extends BeanPropertyMatcher<T> { @@ -11,15 +11,21 @@ public class SelectorMatcher<T extends Selector<?>> extends BeanPropertyMatcher< super(type); } - public static SelectorMatcher<Selector<?>> selector(Matcher<Traversal>... traversals) { + public static SelectorMatcher<Selector<RDFNode>> selector(Matcher<Traversal>... traversals) { if (traversals.length == 0) { - return new SelectorMatcher<Selector<?>>(NoopSelector.class); + return new SelectorMatcher<Selector<RDFNode>>(NoopSelector.class); } - SelectorMatcher<Selector<?>> m = new SelectorMatcher<Selector<?>>(TraversingSelector.class); + SelectorMatcher<Selector<RDFNode>> m = new SelectorMatcher<Selector<RDFNode>>(TraversingSelector.class); m.addRequiredProperty("traversals", hasItems(traversals)); return m; } + public static <R> SelectorMatcher<UnionSelector<R>> unionSelector(Matcher<Selector<R>>... selectors) { + SelectorMatcher<UnionSelector<R>> m = new SelectorMatcher(UnionSelector.class); + m.addRequiredProperty("selectors", hasItems(selectors)); + return m; + } + public <A> SelectorMatcher<Selector<?>> withAdaptation(Matcher<? extends Adaptation<A>> adaptation) { SelectorMatcher<Selector<?>> m = new SelectorMatcher(SelectorWithAdaptation.class); m.addRequiredProperty("baseSelector", this); diff --git a/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorParserUnitTest.java b/src/test/java/au/com/miskinhill/rdftemplate/selector/SelectorParserUnitTest.java @@ -2,24 +2,24 @@ package au.com.miskinhill.rdftemplate.selector; import static au.com.miskinhill.rdftemplate.selector.AdaptationMatcher.*; import static au.com.miskinhill.rdftemplate.selector.PredicateMatcher.*; -import static au.com.miskinhill.rdftemplate.selector.SelectorMatcher.selector; +import static au.com.miskinhill.rdftemplate.selector.SelectorMatcher.*; import static au.com.miskinhill.rdftemplate.selector.TraversalMatcher.traversal; - import static org.junit.Assert.assertThat; +import com.hp.hpl.jena.rdf.model.RDFNode; import org.junit.Test; public class SelectorParserUnitTest { @Test public void shouldRecogniseSingleTraversal() throws Exception { - Selector<?> selector = SelectorParser.parse("dc:creator"); + Selector<RDFNode> selector = SelectorParser.parse("dc:creator").withResultType(RDFNode.class); assertThat(selector, selector(traversal("dc", "creator"))); } @Test public void shouldRecogniseMultipleTraversals() throws Exception { - Selector<?> selector = SelectorParser.parse("dc:creator/foaf:name"); + Selector<RDFNode> selector = SelectorParser.parse("dc:creator/foaf:name").withResultType(RDFNode.class); assertThat(selector, selector( traversal("dc", "creator"), traversal("foaf", "name"))); @@ -27,7 +27,7 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseInverseTraversal() throws Exception { - Selector<?> selector = SelectorParser.parse("!dc:isPartOf/!dc:isPartOf"); + Selector<RDFNode> selector = SelectorParser.parse("!dc:isPartOf/!dc:isPartOf").withResultType(RDFNode.class); assertThat(selector, selector( traversal("dc", "isPartOf").inverse(), traversal("dc", "isPartOf").inverse())); @@ -35,7 +35,7 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseSortOrder() throws Exception { - Selector<?> selector = SelectorParser.parse("!mhs:isIssueOf(mhs:publicationDate#comparable-lv)"); + Selector<RDFNode> selector = SelectorParser.parse("!mhs:isIssueOf(mhs:publicationDate#comparable-lv)").withResultType(RDFNode.class); assertThat(selector, selector( traversal("mhs", "isIssueOf").inverse() .withSortOrder(selector(traversal("mhs", "publicationDate")) @@ -44,7 +44,7 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseReverseSortOrder() throws Exception { - Selector<?> selector = SelectorParser.parse("!mhs:isIssueOf(~mhs:publicationDate#comparable-lv)"); + Selector<RDFNode> selector = SelectorParser.parse("!mhs:isIssueOf(~mhs:publicationDate#comparable-lv)").withResultType(RDFNode.class); assertThat(selector, selector( traversal("mhs", "isIssueOf").inverse() .withSortOrder(selector(traversal("mhs", "publicationDate")) @@ -54,7 +54,7 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseComplexSortOrder() throws Exception { - Selector<?> selector = SelectorParser.parse("!mhs:reviews(dc:isPartOf/mhs:publicationDate#comparable-lv)"); + Selector<RDFNode> selector = SelectorParser.parse("!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")) @@ -86,8 +86,9 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseUriPrefixPredicate() throws Exception { - Selector<?> selector = SelectorParser.parse( - "!mhs:isIssueOf[uri-prefix='http://miskinhill.com.au/journals/'](~mhs:publicationDate#comparable-lv)"); + Selector<RDFNode> selector = SelectorParser.parse( + "!mhs:isIssueOf[uri-prefix='http://miskinhill.com.au/journals/'](~mhs:publicationDate#comparable-lv)") + .withResultType(RDFNode.class); assertThat(selector, selector( traversal("mhs", "isIssueOf") .inverse() @@ -99,8 +100,9 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseSubscript() throws Exception { - Selector<?> selector = SelectorParser.parse( - "!mhs:isIssueOf(~mhs:publicationDate#comparable-lv)[0]/mhs:coverThumbnail#uri"); + Selector<String> selector = SelectorParser.parse( + "!mhs:isIssueOf(~mhs:publicationDate#comparable-lv)[0]/mhs:coverThumbnail#uri") + .withResultType(String.class); assertThat(selector, selector( traversal("mhs", "isIssueOf") .inverse() @@ -114,13 +116,38 @@ public class SelectorParserUnitTest { @Test public void shouldRecogniseLVAdaptation() throws Exception { - Selector<?> selector = SelectorParser.parse("dc:language/lingvoj:iso1#lv"); + Selector<Object> selector = SelectorParser.parse("dc:language/lingvoj:iso1#lv").withResultType(Object.class); assertThat(selector, selector( traversal("dc", "language"), traversal("lingvoj", "iso1")) .withAdaptation(lvAdaptation())); } + @Test + public void shouldRecogniseTypePredicate() throws Exception { + Selector<RDFNode> selector = SelectorParser.parse("!dc:creator[type=mhs:Review]").withResultType(RDFNode.class); + assertThat(selector, selector( + traversal("dc", "creator").inverse().withPredicate(typePredicate("mhs", "Review")))); + } + + @Test + public void shouldRecogniseAndCombinationOfPredicates() throws Exception { + Selector<RDFNode> selector = SelectorParser.parse("!dc:creator[type=mhs:Review and uri-prefix='http://miskinhill.com.au/journals/']").withResultType(RDFNode.class); + assertThat(selector, selector( + traversal("dc", "creator").inverse() + .withPredicate(booleanAndPredicate( + typePredicate("mhs", "Review"), + uriPrefixPredicate("http://miskinhill.com.au/journals/"))))); + } + + @Test + public void shouldRecogniseUnion() throws Exception { + Selector<RDFNode> selector = SelectorParser.parse("!dc:creator | !mhs:translator").withResultType(RDFNode.class); + assertThat((UnionSelector<RDFNode>) selector, unionSelector( + selector(traversal("dc", "creator").inverse()), + selector(traversal("mhs", "translator").inverse()))); + } + @Test(expected = InvalidSelectorSyntaxException.class) public void shouldThrowForInvalidSyntax() throws Exception { SelectorParser.parse("dc:creator]["); // this is a parser error diff --git a/src/test/resources/au/com/miskinhill/rdftemplate/conditional.xml b/src/test/resources/au/com/miskinhill/rdftemplate/conditional.xml @@ -0,0 +1,10 @@ +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> +<body> + +<span rdf:if="mhs:biographicalNotes">attribute test</span> + +<rdf:if test="mhs:biographicalNotes">element test</rdf:if> + +</body> +</html> +\ No newline at end of file diff --git a/src/test/resources/au/com/miskinhill/rdftemplate/test-data.xml b/src/test/resources/au/com/miskinhill/rdftemplate/test-data.xml @@ -52,6 +52,7 @@ <mhs:Article rdf:about="http://miskinhill.com.au/journals/test/1:1/article"> <dc:isPartOf rdf:resource="http://miskinhill.com.au/journals/test/1:1/"/> <dc:creator rdf:resource="http://miskinhill.com.au/authors/test-author"/> + <mhs:translator rdf:resource="http://miskinhill.com.au/authors/another-author"/> <dc:title rdf:parseType="Literal"><span xmlns="http://www.w3.org/1999/xhtml" lang="en"><em>Moscow 1937</em>: the interpreter’s story</span></dc:title> <mhs:startPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">5</mhs:startPage> <mhs:endPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">35</mhs:endPage> @@ -60,6 +61,7 @@ <foaf:name>Test Author</foaf:name> <foaf:surname>Author</foaf:surname> <foaf:givenNames>Test</foaf:givenNames> + <mhs:biographicalNotes>This person does stuff.</mhs:biographicalNotes> </mhs:Author> <mhs:Journal rdf:about="http://miskinhill.com.au/cited/journals/asdf/"> <dc:title xml:lang="en">A Cited Journal</dc:title>