commit 29902dea0fa92d278277ad18ce69e906ab08d59d parent 3942ee75f0308d5df2da41aff3bdb4cec48d6d94 Author: Dan Callaghan <djc@djc.id.au> Date: Sun, 29 Nov 2009 16:38:35 +1000 more verbose exceptions Diffstat:
10 files changed, 367 insertions(+), 91 deletions(-) diff --git a/src/main/java/au/com/miskinhill/rdftemplate/ContentAction.java b/src/main/java/au/com/miskinhill/rdftemplate/ContentAction.java @@ -0,0 +1,67 @@ +package au.com.miskinhill.rdftemplate; + +import java.util.Set; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; + +import com.hp.hpl.jena.rdf.model.Literal; +import com.hp.hpl.jena.rdf.model.RDFNode; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.ToStringBuilder; + +import au.com.miskinhill.rdftemplate.selector.Selector; + +public class ContentAction extends TemplateAction { + + public static final String ACTION_NAME = "content"; + public static final QName ACTION_QNAME = new QName(TemplateInterpolator.NS, ACTION_NAME); + 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"; + + private final StartElement start; + private final Selector<?> selector; + + public ContentAction(StartElement start, Selector<?> selector) { + this.start = start; + this.selector = selector; + } + + public void evaluate(TemplateInterpolator interpolator, RDFNode node, XMLEventDestination writer, XMLEventFactory eventFactory) + throws XMLStreamException { + Object replacement = selector.singleResult(node); + StartElement start = interpolator.interpolateAttributes(this.start, node); + Set<Attribute> attributes = interpolator.cloneAttributesWithout(start, ACTION_QNAME); + if (replacement instanceof Literal) { + Literal literal = (Literal) replacement; + if (!StringUtils.isEmpty(literal.getLanguage())) { + attributes.add(eventFactory.createAttribute(XML_LANG_QNAME, ((Literal) replacement).getLanguage())); + if (start.getName().getNamespaceURI().equals(XHTML_NS_URI)) { + String xhtmlPrefixInContext = start.getNamespaceContext().getPrefix(XHTML_NS_URI); + QName xhtmlLangQNameForContext; // ugh + if (xhtmlPrefixInContext.isEmpty()) + xhtmlLangQNameForContext = new QName("lang"); + else + xhtmlLangQNameForContext = new QName(XHTML_NS_URI, "lang", xhtmlPrefixInContext); + attributes.add(eventFactory.createAttribute(xhtmlLangQNameForContext, literal.getLanguage())); + } + } + } + writer.add(eventFactory.createStartElement(start.getName(), attributes.iterator(), start.getNamespaces())); + interpolator.writeTreeForContent(writer, replacement); + writer.add(eventFactory.createEndElement(start.getName(), start.getNamespaces())); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("start", start) + .append("selector", selector) + .toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/ForAction.java b/src/main/java/au/com/miskinhill/rdftemplate/ForAction.java @@ -0,0 +1,41 @@ +package au.com.miskinhill.rdftemplate; + +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import com.hp.hpl.jena.rdf.model.RDFNode; +import org.apache.commons.lang.builder.ToStringBuilder; + +import au.com.miskinhill.rdftemplate.selector.Selector; + +public class ForAction extends TemplateAction { + + public static final String ACTION_NAME = "for"; + public static final QName ACTION_QNAME = new QName(TemplateInterpolator.NS, ACTION_NAME); + + private final List<XMLEvent> tree; + private final Selector<RDFNode> selector; + + public ForAction(List<XMLEvent> tree, Selector<RDFNode> selector) { + this.tree = tree; + this.selector = selector; + } + + public void evaluate(TemplateInterpolator interpolator, RDFNode node, XMLEventDestination writer) + throws XMLStreamException { + for (RDFNode eachNode: selector.result(node)) { + interpolator.interpolate(tree.iterator(), eachNode, writer); + } + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("selector", selector) + .toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/IfAction.java b/src/main/java/au/com/miskinhill/rdftemplate/IfAction.java @@ -0,0 +1,46 @@ +package au.com.miskinhill.rdftemplate; + +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.hp.hpl.jena.rdf.model.RDFNode; + +import au.com.miskinhill.rdftemplate.selector.Selector; + +public class IfAction extends TemplateAction { + + public static final String ACTION_NAME = "if"; + public static final QName ACTION_QNAME = new QName(TemplateInterpolator.NS, ACTION_NAME); + + private final List<XMLEvent> tree; + private final Selector<?> condition; + private final boolean negate; + + public IfAction(List<XMLEvent> tree, Selector<?> condition, boolean negate) { + this.tree = tree; + this.condition = condition; + this.negate = negate; + } + + public void evaluate(TemplateInterpolator interpolator, RDFNode node, XMLEventDestination writer) + throws XMLStreamException { + List<?> selectorResult = condition.result(node); + if (negate ? selectorResult.isEmpty() : !selectorResult.isEmpty()) { + interpolator.interpolate(tree.iterator(), node, writer); + } + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("condition", condition) + .append("negate", negate) + .toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/JoinAction.java b/src/main/java/au/com/miskinhill/rdftemplate/JoinAction.java @@ -0,0 +1,51 @@ +package au.com.miskinhill.rdftemplate; + +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.hp.hpl.jena.rdf.model.RDFNode; + +import au.com.miskinhill.rdftemplate.selector.Selector; + +public class JoinAction extends TemplateAction { + + public static final String ACTION_NAME = "join"; + public static final QName ACTION_QNAME = new QName(TemplateInterpolator.NS, ACTION_NAME); + + private final List<XMLEvent> tree; + private final Selector<RDFNode> selector; + private final String separator; + + public JoinAction(List<XMLEvent> tree, Selector<RDFNode> selector, String separator) { + this.tree = tree; + this.selector = selector; + this.separator = separator; + } + + public void evaluate(TemplateInterpolator interpolator, RDFNode node, XMLEventDestination writer, XMLEventFactory eventFactory) + throws XMLStreamException { + boolean first = true; + for (RDFNode eachNode: selector.result(node)) { + if (!first) { + writer.add(eventFactory.createCharacters(separator)); + } + interpolator.interpolate(tree.iterator(), eachNode, writer); + first = false; + } + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("selector", selector) + .append("separator", separator) + .toString(); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateAction.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateAction.java @@ -0,0 +1,5 @@ +package au.com.miskinhill.rdftemplate; + +public abstract class TemplateAction { + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolationException.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolationException.java @@ -0,0 +1,23 @@ +package au.com.miskinhill.rdftemplate; + +import javax.xml.stream.Location; + +import com.hp.hpl.jena.rdf.model.RDFNode; + +public class TemplateInterpolationException extends RuntimeException { + + private static final long serialVersionUID = -1472104970210074672L; + + public TemplateInterpolationException(Location location, TemplateAction action, RDFNode node, Throwable cause) { + super("Exception evaluating action [" + action + "] " + + "at location [" + location.getLineNumber() + "," + location.getColumnNumber() + "] " + + "with context node " + node, cause); + } + + public TemplateInterpolationException(Location location, String selectorExpression, RDFNode node, Throwable cause) { + super("Exception evaluating selector expression [" + selectorExpression + "] " + + "at location [" + location.getLineNumber() + "," + location.getColumnNumber() + "] " + + "with context node " + node, cause); + } + +} diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java @@ -14,7 +14,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; @@ -32,24 +31,14 @@ import javax.xml.stream.events.XMLEvent; import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.RDFNode; -import org.apache.commons.lang.StringUtils; +import au.com.miskinhill.rdftemplate.selector.InvalidSelectorSyntaxException; import au.com.miskinhill.rdftemplate.selector.Selector; import au.com.miskinhill.rdftemplate.selector.SelectorFactory; public class TemplateInterpolator { public static final String NS = "http://code.miskinhill.com.au/rdftemplate/"; - public static final String CONTENT_ACTION = "content"; - 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); - public static final String JOIN_ACTION = "join"; - private static final QName JOIN_ACTION_QNAME = new QName(NS, JOIN_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"; private final XMLInputFactory inputFactory = XMLInputFactory.newInstance(); private final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); @@ -105,108 +94,124 @@ public class TemplateInterpolator { switch (event.getEventType()) { case XMLStreamConstants.START_ELEMENT: { StartElement start = (StartElement) event; - if (start.getName().equals(IF_ACTION_QNAME)) { + if (start.getName().equals(IfAction.ACTION_QNAME)) { Attribute testAttribute = start.getAttributeByName(new QName("test")); Attribute notAttribute = start.getAttributeByName(new QName("not")); String condition; boolean negate = false; if (testAttribute != null && notAttribute != null) - throw new TemplateSyntaxException("test and not attribute on rdf:if are mutually exclusive"); + throw new TemplateSyntaxException(start.getLocation(), "test and not attribute on rdf:if are mutually exclusive"); else if (testAttribute != null) condition = testAttribute.getValue(); else if (notAttribute != null) { condition = notAttribute.getValue(); negate = true; } else - throw new TemplateSyntaxException("rdf:if must have a test attribute or a not attribute"); - List<?> selectorResult = selectorFactory.get(condition).result(node); - if (negate ? !selectorResult.isEmpty() : selectorResult.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); + throw new TemplateSyntaxException(start.getLocation(), "rdf:if must have a test attribute or a not attribute"); + Selector<?> conditionSelector; + try { + conditionSelector = selectorFactory.get(condition); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(start.getLocation(), e); + } + List<XMLEvent> tree = consumeTree(start, reader); + // discard enclosing rdf:if + tree.remove(tree.size() - 1); + tree.remove(0); + IfAction action = new IfAction(tree, conditionSelector, negate); + try { + action.evaluate(this, node, writer); + } catch (Exception e) { + throw new TemplateInterpolationException(start.getLocation(), action, node, e); } - } else if (start.getName().equals(JOIN_ACTION_QNAME)) { + } else if (start.getName().equals(JoinAction.ACTION_QNAME)) { Attribute eachAttribute = start.getAttributeByName(new QName("each")); if (eachAttribute == null) - throw new TemplateSyntaxException("rdf:join must have an each attribute"); + throw new TemplateSyntaxException(start.getLocation(), "rdf:join must have an each attribute"); String separator = ""; Attribute separatorAttribute = start.getAttributeByName(new QName("separator")); if (separatorAttribute != null) separator = separatorAttribute.getValue(); - Selector<RDFNode> selector = selectorFactory.get(eachAttribute.getValue()).withResultType(RDFNode.class); + Selector<RDFNode> selector; + try { + selector = selectorFactory.get(eachAttribute.getValue()).withResultType(RDFNode.class); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(start.getLocation(), e); + } List<XMLEvent> events = consumeTree(start, reader); // discard enclosing rdf:join events.remove(events.size() - 1); events.remove(0); - boolean first = true; - for (RDFNode eachNode: selector.result(node)) { - if (!first) { - writer.add(eventFactory.createCharacters(separator)); - } - interpolate(events.iterator(), eachNode, writer); - first = false; + JoinAction action = new JoinAction(events, selector, separator); + try { + action.evaluate(this, node, writer, eventFactory); + } catch (Exception e) { + throw new TemplateInterpolationException(start.getLocation(), action, node, e); } - } else if (start.getName().equals(FOR_ACTION_QNAME)) { + } else if (start.getName().equals(ForAction.ACTION_QNAME)) { Attribute eachAttribute = start.getAttributeByName(new QName("each")); if (eachAttribute == null) - throw new TemplateSyntaxException("rdf:for must have an each attribute"); + throw new TemplateSyntaxException(start.getLocation(), "rdf:for must have an each attribute"); + Selector<RDFNode> selector; + try { + selector = selectorFactory.get(eachAttribute.getValue()).withResultType(RDFNode.class); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(start.getLocation(), e); + } List<XMLEvent> events = consumeTree(start, reader); // discard enclosing rdf:for events.remove(events.size() - 1); events.remove(0); - Selector<RDFNode> selector = selectorFactory.get(eachAttribute.getValue()).withResultType(RDFNode.class); - for (RDFNode subNode : selector.result(node)) { - interpolate(events.iterator(), subNode, writer); + ForAction action = new ForAction(events, selector); + try { + action.evaluate(this, node, writer); + } catch (Exception e) { + throw new TemplateInterpolationException(start.getLocation(), action, node, e); } } else { - Attribute ifAttribute = start.getAttributeByName(IF_ACTION_QNAME); - Attribute contentAttribute = start.getAttributeByName(CONTENT_ACTION_QNAME); - Attribute forAttribute = start.getAttributeByName(FOR_ACTION_QNAME); + Attribute ifAttribute = start.getAttributeByName(IfAction.ACTION_QNAME); + Attribute contentAttribute = start.getAttributeByName(ContentAction.ACTION_QNAME); + Attribute forAttribute = start.getAttributeByName(ForAction.ACTION_QNAME); if (ifAttribute != null) { - Selector<?> selector = selectorFactory.get(ifAttribute.getValue()); - if (selector.result(node).isEmpty()) { - consumeTree(start, reader); - break; + Selector<?> selector; + try { + selector = selectorFactory.get(ifAttribute.getValue()); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(ifAttribute.getLocation(), e); } - start = cloneStart(start, cloneAttributesWithout(start, IF_ACTION_QNAME), cloneNamespacesWithoutRdf(start)); - } - if (contentAttribute != null && forAttribute != null) { - throw new TemplateSyntaxException("rdf:for and rdf:content cannot both be present on an element"); + start = cloneStart(start, cloneAttributesWithout(start, IfAction.ACTION_QNAME), cloneNamespacesWithoutRdf(start)); + IfAction action = new IfAction(consumeTree(start, reader), selector, false); + action.evaluate(this, node, writer); + } else if (contentAttribute != null && forAttribute != null) { + throw new TemplateSyntaxException(start.getLocation(), "rdf:for and rdf:content cannot both be present on an element"); } else if (contentAttribute != null) { - consumeTree(start, reader); - Selector<?> selector = selectorFactory.get(contentAttribute.getValue()); - Object replacement = selector.singleResult(node); - start = interpolateAttributes(start, node); - Set<Attribute> attributes = cloneAttributesWithout(start, CONTENT_ACTION_QNAME); - if (replacement instanceof Literal) { - Literal literal = (Literal) replacement; - if (!StringUtils.isEmpty(literal.getLanguage())) { - attributes.add(eventFactory.createAttribute(XML_LANG_QNAME, ((Literal) replacement).getLanguage())); - if (start.getName().getNamespaceURI().equals(XHTML_NS_URI)) { - String xhtmlPrefixInContext = start.getNamespaceContext().getPrefix(XHTML_NS_URI); - QName xhtmlLangQNameForContext; // ugh - if (xhtmlPrefixInContext.isEmpty()) - xhtmlLangQNameForContext = new QName("lang"); - else - xhtmlLangQNameForContext = new QName(XHTML_NS_URI, "lang", xhtmlPrefixInContext); - attributes.add(eventFactory.createAttribute(xhtmlLangQNameForContext, literal.getLanguage())); - } - } + consumeTree(start, reader); // discard + Selector<?> selector; + try { + selector = selectorFactory.get(contentAttribute.getValue()); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(contentAttribute.getLocation(), e); + } + ContentAction action = new ContentAction(start, selector); + try { + action.evaluate(this, node, writer, eventFactory); + } catch (Exception e) { + throw new TemplateInterpolationException(contentAttribute.getLocation(), action, node, e); } - writer.add(eventFactory.createStartElement(start.getName(), attributes.iterator(), start.getNamespaces())); - writeTreeForContent(writer, replacement); - writer.add(eventFactory.createEndElement(start.getName(), start.getNamespaces())); } else if (forAttribute != null) { - start = cloneStart(start, cloneAttributesWithout(start, FOR_ACTION_QNAME), cloneNamespacesWithoutRdf(start)); + Selector<RDFNode> selector; + try { + selector = selectorFactory.get(forAttribute.getValue()).withResultType(RDFNode.class); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(forAttribute.getLocation(), e); + } + start = cloneStart(start, cloneAttributesWithout(start, ForAction.ACTION_QNAME), cloneNamespacesWithoutRdf(start)); List<XMLEvent> tree = consumeTree(start, reader); - Selector<RDFNode> selector = selectorFactory.get(forAttribute.getValue()).withResultType(RDFNode.class); - for (RDFNode subNode : selector.result(node)) { - interpolate(tree.iterator(), subNode, writer); + ForAction action = new ForAction(tree, selector); + try { + action.evaluate(this, node, writer); + } catch (Exception e) { + throw new TemplateInterpolationException(forAttribute.getLocation(), action, node, e); } } else { start = interpolateAttributes(start, node); @@ -257,13 +262,18 @@ public class TemplateInterpolator { } @SuppressWarnings("unchecked") - private StartElement interpolateAttributes(StartElement start, RDFNode node) { + protected StartElement interpolateAttributes(StartElement start, RDFNode node) { Set<Attribute> replacementAttributes = new LinkedHashSet<Attribute>(); for (Iterator<Attribute> it = start.getAttributes(); it.hasNext(); ) { Attribute attribute = it.next(); String replacementValue = attribute.getValue(); - if (!attribute.getName().getNamespaceURI().equals(NS)) // skip rdf: attributes - replacementValue = interpolateString(attribute.getValue(), node); + if (!attribute.getName().getNamespaceURI().equals(NS)) { // skip rdf: attributes + try { + replacementValue = interpolateString(attribute.getValue(), node); + } catch (Exception e) { + throw new TemplateInterpolationException(attribute.getLocation(), attribute.getValue(), node, e); + } + } replacementAttributes.add(eventFactory.createAttribute(attribute.getName(), replacementValue)); } @@ -333,13 +343,23 @@ public class TemplateInterpolator { writer.add(eventFactory.createCharacters(template.substring(lastAppendedPos, matcher.start()))); lastAppendedPos = matcher.end(); String expression = matcher.group(1); - Object replacement = selectorFactory.get(expression).singleResult(node); - writeTreeForContent(writer, replacement); + Selector<?> selector; + try { + selector = selectorFactory.get(expression); + } catch (InvalidSelectorSyntaxException e) { + throw new TemplateSyntaxException(characters.getLocation(), e); + } + try { + Object replacement = selector.singleResult(node); + writeTreeForContent(writer, replacement); + } catch (Exception e) { + throw new TemplateInterpolationException(characters.getLocation(), expression, node, e); + } } writer.add(eventFactory.createCharacters(template.substring(lastAppendedPos))); } - private void writeTreeForContent(XMLEventDestination writer, Object replacement) + protected void writeTreeForContent(XMLEventDestination writer, Object replacement) throws XMLStreamException { if (replacement instanceof RDFNode) { RDFNode replacementNode = (RDFNode) replacement; @@ -363,7 +383,7 @@ public class TemplateInterpolator { } @SuppressWarnings("unchecked") - private Set<Attribute> cloneAttributesWithout(StartElement start, QName omit) { + protected Set<Attribute> cloneAttributesWithout(StartElement start, QName omit) { // clone attributes, but without rdf:content Set<Attribute> attributes = new LinkedHashSet<Attribute>(); for (Iterator<Attribute> it = start.getAttributes(); it.hasNext(); ) { diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateSyntaxException.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateSyntaxException.java @@ -1,15 +1,22 @@ package au.com.miskinhill.rdftemplate; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; + public class TemplateSyntaxException extends RuntimeException { private static final long serialVersionUID = 6518982504570154030L; - public TemplateSyntaxException(String message) { - super(message); + public TemplateSyntaxException(Location location, String message) { + super("[location " + location.getLineNumber() + "," + location.getColumnNumber() + "] " + message); + } + + public TemplateSyntaxException(Location location, Throwable cause) { + super("[location " + location.getLineNumber() + "," + location.getColumnNumber() + "]", cause); } - public TemplateSyntaxException(Throwable cause) { - super(cause); + public TemplateSyntaxException(XMLStreamException e) { + super(e); } } diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorComparator.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorComparator.java @@ -37,8 +37,20 @@ public class SelectorComparator<T extends Comparable<T>> implements Comparator<R @Override public int compare(RDFNode left, RDFNode right) { - T leftKey = selector.singleResult(left); - T rightKey = selector.singleResult(right); + T leftKey; + try { + leftKey = selector.singleResult(left); + } catch (SelectorEvaluationException e) { + throw new SelectorEvaluationException("Exception evaluating selector [" + selector + "] " + + "with context node [" + left + "] for comparison", e); + } + T rightKey; + try { + rightKey = selector.singleResult(right); + } catch (SelectorEvaluationException e) { + throw new SelectorEvaluationException("Exception evaluating selector [" + selector + "] " + + "with context node [" + right + "] for comparison", e); + } int result = leftKey.compareTo(rightKey); return reversed ? -result : result; } diff --git a/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorEvaluationException.java b/src/main/java/au/com/miskinhill/rdftemplate/selector/SelectorEvaluationException.java @@ -2,10 +2,14 @@ package au.com.miskinhill.rdftemplate.selector; public class SelectorEvaluationException extends RuntimeException { - private static final long serialVersionUID = -398277800899471325L; + private static final long serialVersionUID = -398277800899471326L; public SelectorEvaluationException(String message) { super(message); } + + public SelectorEvaluationException(String message, Throwable cause) { + super(message, cause); + } }