Author: rhauch
Date: 2009-10-03 00:07:41 -0400 (Sat, 03 Oct 2009)
New Revision: 1285
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
Log:
DNA-467 Added XPath parser with associated unit tests, though this doesn't yet convert
the parsed XPath AST into a query using the Abstract Query Model
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java 2009-10-02
17:35:34 UTC (rev 1284)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java 2009-10-03
04:07:41 UTC (rev 1285)
@@ -437,7 +437,7 @@
*
* @return the current token's position; never null
* @throws IllegalStateException if this method was called before the stream was
{@link #start() started}
- * @throws NoSuchElementException if there is no previous token
+ * @throws NoSuchElementException if there is no next token
*/
public Position nextPosition() {
return currentToken().position();
@@ -1047,7 +1047,7 @@
ListIterator<Token> iter =
tokens.listIterator(tokenIterator.previousIndex());
if (!iter.hasNext()) return false;
Token token = iter.next();
- if (currentExpectedType != ANY_TYPE || currentToken().type() !=
currentExpectedType) return false;
+ if (currentExpectedType != ANY_TYPE && currentToken().type() !=
currentExpectedType) return false;
for (int nextExpectedType : expectedTypeForNextTokens) {
if (!iter.hasNext()) return false;
token = iter.next();
@@ -1366,6 +1366,22 @@
boolean isNextValidXmlCharacter();
/**
+ * Determine if the next character on the sream is a {@link
XmlCharacters#isValidName(int) valid XML NCName character}.
+ * This method does <i>not</i> advance the stream.
+ *
+ * @return true if there is a {@link #next() next} character and it is a valid
XML Name character, or false otherwise
+ */
+ boolean isNextValidXmlNameCharacter();
+
+ /**
+ * Determine if the next character on the sream is a {@link
XmlCharacters#isValidNcName(int) valid XML NCName character}.
+ * This method does <i>not</i> advance the stream.
+ *
+ * @return true if there is a {@link #next() next} character and it is a valid
XML NCName character, or false otherwise
+ */
+ boolean isNextValidXmlNcNameCharacter();
+
+ /**
* Determine if the next character on the sream is the supplied value. This
method does <i>not</i> advance the stream.
*
* @param c the character value to compare to the next character on the stream
@@ -1882,6 +1898,26 @@
int nextIndex = lastIndex + 1;
return nextIndex <= maxIndex &&
XmlCharacters.isValid(content[nextIndex]);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.common.text.TokenStream.CharacterStream#isNextValidXmlNameCharacter()
+ */
+ public boolean isNextValidXmlNameCharacter() {
+ int nextIndex = lastIndex + 1;
+ return nextIndex <= maxIndex &&
XmlCharacters.isValidName(content[nextIndex]);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.common.text.TokenStream.CharacterStream#isNextValidXmlNcNameCharacter()
+ */
+ public boolean isNextValidXmlNcNameCharacter() {
+ int nextIndex = lastIndex + 1;
+ return nextIndex <= maxIndex &&
XmlCharacters.isValidNcName(content[nextIndex]);
+ }
}
/**
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
(rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java 2009-10-03 04:07:41 UTC
(rev 1285)
@@ -0,0 +1,1132 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.query.model.Operator;
+
+/**
+ *
+ */
+public class XPath {
+
+ public static enum NodeComparisonOperator {
+ IS,
+ PRECEDES,
+ FOLLOWS;
+ }
+
+ public static interface Component {
+ }
+
+ public static abstract class UnaryComponent implements Component {
+ protected final Component wrapped;
+
+ public UnaryComponent( Component wrapped ) {
+ this.wrapped = wrapped;
+ }
+ }
+
+ public static class Negation extends UnaryComponent {
+ public Negation( Component wrapped ) {
+ super(wrapped);
+ }
+
+ public Component getNegated() {
+ return wrapped;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "-" + wrapped;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Negation) {
+ Negation that = (Negation)obj;
+ return this.wrapped.equals(that.wrapped);
+ }
+ return false;
+ }
+ }
+
+ public static abstract class BinaryComponent implements Component {
+ private final Component left;
+ private final Component right;
+
+ public BinaryComponent( Component left,
+ Component right ) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * @return left
+ */
+ public Component getLeft() {
+ return left;
+ }
+
+ /**
+ * @return right
+ */
+ public Component getRight() {
+ return right;
+ }
+ }
+
+ public static class Comparison extends BinaryComponent {
+ private final Operator operator;
+
+ public Comparison( Component left,
+ Operator operator,
+ Component right ) {
+ super(left, right);
+ this.operator = operator;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " " + operator + " " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Comparison) {
+ Comparison that = (Comparison)obj;
+ if (this.operator != that.operator) return false;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class NodeComparison extends BinaryComponent {
+ private final NodeComparisonOperator operator;
+
+ public NodeComparison( Component left,
+ NodeComparisonOperator operator,
+ Component right ) {
+ super(left, right);
+ this.operator = operator;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " " + operator + " " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NodeComparison) {
+ NodeComparison that = (NodeComparison)obj;
+ if (this.operator != that.operator) return false;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class Add extends BinaryComponent {
+ public Add( Component left,
+ Component right ) {
+ super(left, right);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " + " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Add) {
+ Add that = (Add)obj;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class Subtract extends BinaryComponent {
+ public Subtract( Component left,
+ Component right ) {
+ super(left, right);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " - " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Subtract) {
+ Subtract that = (Subtract)obj;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class And extends BinaryComponent {
+ public And( Component left,
+ Component right ) {
+ super(left, right);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " and " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof And) {
+ And that = (And)obj;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class Union extends BinaryComponent {
+ public Union( Component left,
+ Component right ) {
+ super(left, right);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " union " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Union) {
+ Union that = (Union)obj;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class Or extends BinaryComponent {
+ public Or( Component left,
+ Component right ) {
+ super(left, right);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getLeft() + " or " + getRight();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Or) {
+ Or that = (Or)obj;
+ return this.getLeft().equals(that.getLeft()) &&
this.getRight().equals(that.getRight());
+ }
+ return false;
+ }
+ }
+
+ public static class ContextItem implements Component {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ return obj == this || obj instanceof ContextItem;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return ".";
+ }
+ }
+
+ public static class Literal implements Component {
+ private final String value;
+
+ public Literal( String value ) {
+ this.value = value;
+ }
+
+ /**
+ * @return value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Literal) {
+ return this.value.equals(((Literal)obj).value);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ public static class FunctionCall implements Component {
+ private final NameTest name;
+ private final List<Component> arguments;
+
+ public FunctionCall( NameTest name,
+ List<Component> arguments ) {
+ assert name != null;
+ assert arguments != null;
+ this.name = name;
+ this.arguments = arguments;
+ }
+
+ /**
+ * @return name
+ */
+ public NameTest getName() {
+ return name;
+ }
+
+ /**
+ * @return arguments
+ */
+ public List<Component> getParameters() {
+ return arguments;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FunctionCall) {
+ FunctionCall that = (FunctionCall)obj;
+ return this.name.equals(that.name) &&
this.arguments.equals(that.arguments);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return name + "(" + asString(arguments, ",") +
")";
+ }
+ }
+
+ protected static String asString( Iterable<?> components,
+ String delimiter ) {
+ StringBuilder sb = new StringBuilder();
+ for (Object component : components) {
+ if (sb.length() != 0) sb.append(delimiter);
+ sb.append(component);
+ }
+ return sb.toString();
+ }
+
+ public static class PathExpression implements Component {
+ private final List<StepExpression> steps;
+ private final boolean relative;
+
+ public PathExpression( boolean relative,
+ List<StepExpression> steps ) {
+ this.steps = steps;
+ this.relative = relative;
+ }
+
+ /**
+ * @return relative
+ */
+ public boolean isRelative() {
+ return relative;
+ }
+
+ /**
+ * @return steps
+ */
+ public List<StepExpression> getSteps() {
+ return steps;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PathExpression) {
+ PathExpression that = (PathExpression)obj;
+ return this.relative == that.relative &&
this.steps.equals(that.steps);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return (relative ? "" : "/") + asString(steps,
"/");
+ }
+ }
+
+ public static interface StepExpression extends Component {
+ }
+
+ public static class FilterStep implements StepExpression {
+ private final Component primaryExpression;
+ private final List<Component> predicates;
+
+ public FilterStep( Component primaryExpression,
+ List<Component> predicates ) {
+ assert primaryExpression != null;
+ assert predicates != null;
+ this.primaryExpression = primaryExpression;
+ this.predicates = predicates;
+ }
+
+ /**
+ * @return nodeTest
+ */
+ public Component getPrimaryExpression() {
+ return primaryExpression;
+ }
+
+ /**
+ * @return predicates
+ */
+ public List<Component> getPredicates() {
+ return predicates;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FilterStep) {
+ FilterStep that = (FilterStep)obj;
+ return this.primaryExpression.equals(that.primaryExpression) &&
this.predicates.equals(that.predicates);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return primaryExpression + (predicates.isEmpty() ? "" :
predicates.toString());
+ }
+ }
+
+ public static class DescendantOrSelf implements StepExpression {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ return obj == this || obj instanceof DescendantOrSelf;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "descendant-or-self::node()";
+ }
+ }
+
+ public static class AxisStep implements StepExpression {
+ private final NodeTest nodeTest;
+ private final List<Component> predicates;
+
+ public AxisStep( NodeTest nodeTest,
+ List<Component> predicates ) {
+ assert nodeTest != null;
+ assert predicates != null;
+ this.nodeTest = nodeTest;
+ this.predicates = predicates;
+ }
+
+ /**
+ * @return nodeTest
+ */
+ public NodeTest getNodeTest() {
+ return nodeTest;
+ }
+
+ /**
+ * @return predicates
+ */
+ public List<Component> getPredicates() {
+ return predicates;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof AxisStep) {
+ AxisStep that = (AxisStep)obj;
+ return this.nodeTest.equals(that.nodeTest) &&
this.predicates.equals(that.predicates);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return nodeTest + (predicates.isEmpty() ? "" :
predicates.toString());
+ }
+ }
+
+ public static class ParenthesizedExpression implements Component {
+ private final Component wrapped;
+
+ public ParenthesizedExpression() {
+ this.wrapped = null;
+ }
+
+ public ParenthesizedExpression( Component wrapped ) {
+ this.wrapped = wrapped; // may be null
+ }
+
+ /**
+ * @return wrapped
+ */
+ public Component getWrapped() {
+ return wrapped;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ParenthesizedExpression) {
+ ParenthesizedExpression that = (ParenthesizedExpression)obj;
+ return this.wrapped.equals(that.wrapped);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "(" + wrapped + ")";
+ }
+ }
+
+ public static interface NodeTest extends Component {
+ }
+
+ public static interface KindTest extends NodeTest {
+ }
+
+ public static class NameTest implements NodeTest {
+ private final String prefixTest;
+ private final String localTest;
+
+ public NameTest( String prefixTest,
+ String localTest ) {
+ this.prefixTest = prefixTest;
+ this.localTest = localTest;
+ }
+
+ /**
+ * @return prefixTest
+ */
+ public String getPrefixTest() {
+ return prefixTest;
+ }
+
+ /**
+ * @return localTest
+ */
+ public String getLocalTest() {
+ return localTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NameTest) {
+ NameTest that = (NameTest)obj;
+ return ObjectUtil.isEqualWithNulls(this.prefixTest, that.prefixTest)
+ && ObjectUtil.isEqualWithNulls(this.localTest,
that.localTest);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String local = localTest != null ? localTest : "*";
+ return prefixTest == null ? local : (prefixTest + ":" + local);
+ }
+ }
+
+ public static class AttributeNameTest implements NodeTest {
+ private final NodeTest nodeTest;
+
+ public AttributeNameTest( NodeTest nodeTest ) {
+ this.nodeTest = nodeTest;
+ }
+
+ /**
+ * @return nodeTest
+ */
+ public NodeTest getNodeTest() {
+ return nodeTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see XPath.NameTest#toString()
+ */
+ @Override
+ public String toString() {
+ return "@" + nodeTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof AttributeNameTest) {
+ AttributeNameTest that = (AttributeNameTest)obj;
+ return this.nodeTest.equals(that.nodeTest);
+ }
+ return false;
+ }
+
+ }
+
+ public static class AnyKindTest implements KindTest {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ return obj == this || obj instanceof AnyKindTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "node()";
+ }
+ }
+
+ public static class TextTest implements KindTest {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ return obj == this || obj instanceof TextTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "text()";
+ }
+ }
+
+ public static class CommentTest implements KindTest {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ return obj == this || obj instanceof CommentTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "comment()";
+ }
+ }
+
+ public static class ProcessingInstructionTest implements KindTest {
+ private final String nameOrStringLiteral;
+
+ public ProcessingInstructionTest( String nameOrStringLiteral ) {
+ this.nameOrStringLiteral = nameOrStringLiteral;
+ }
+
+ /**
+ * @return nameOrStringLiteral
+ */
+ public String getNameOrStringLiteral() {
+ return nameOrStringLiteral;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ProcessingInstructionTest) {
+ ProcessingInstructionTest that = (ProcessingInstructionTest)obj;
+ return this.nameOrStringLiteral.equals(that.nameOrStringLiteral);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "processing-instruction(" + nameOrStringLiteral +
")";
+ }
+ }
+
+ public static class DocumentTest implements KindTest {
+ private KindTest elementOrSchemaElementTest;
+
+ public DocumentTest( ElementTest elementTest ) {
+ CheckArg.isNotNull(elementTest, "elementTest");
+ this.elementOrSchemaElementTest = elementTest;
+ }
+
+ public DocumentTest( SchemaElementTest schemaElementTest ) {
+ CheckArg.isNotNull(schemaElementTest, "schemaElementTest");
+ this.elementOrSchemaElementTest = schemaElementTest;
+ }
+
+ /**
+ * @return elementOrSchemaElementTest
+ */
+ public ElementTest getElementTest() {
+ return elementOrSchemaElementTest instanceof ElementTest ?
(ElementTest)elementOrSchemaElementTest : null;
+ }
+
+ /**
+ * @return elementOrSchemaElementTest
+ */
+ public SchemaElementTest getSchemaElementTest() {
+ return elementOrSchemaElementTest instanceof SchemaElementTest ?
(SchemaElementTest)elementOrSchemaElementTest : null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof DocumentTest) {
+ DocumentTest that = (DocumentTest)obj;
+ return
this.elementOrSchemaElementTest.equals(that.elementOrSchemaElementTest);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "document-node(" + elementOrSchemaElementTest +
")";
+ }
+ }
+
+ public static class AttributeTest implements KindTest {
+ private final NameTest attributeNameOrWildcard;
+ private final NameTest typeName;
+
+ public AttributeTest( NameTest attributeNameOrWildcard,
+ NameTest typeName ) {
+ this.attributeNameOrWildcard = attributeNameOrWildcard;
+ this.typeName = typeName;
+ }
+
+ /**
+ *@return the attribute name, which may be a wilcard
+ */
+ public NameTest getAttributeName() {
+ return attributeNameOrWildcard;
+ }
+
+ /**
+ * @return typeName
+ */
+ public NameTest getTypeName() {
+ return typeName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof AttributeTest) {
+ AttributeTest that = (AttributeTest)obj;
+ return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
+ &&
ObjectUtil.isEqualWithNulls(this.attributeNameOrWildcard, that.attributeNameOrWildcard);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "attribute(" + attributeNameOrWildcard + (typeName != null ?
"," + typeName : "") + ")";
+ }
+ }
+
+ public static class ElementTest implements KindTest {
+ private final NameTest elementNameOrWildcard;
+ private final NameTest typeName;
+
+ public ElementTest( NameTest elementNameOrWildcard,
+ NameTest typeName ) {
+ this.elementNameOrWildcard = elementNameOrWildcard;
+ this.typeName = typeName;
+ }
+
+ /**
+ *@return the element name, which may be a wilcard
+ */
+ public NameTest getElementName() {
+ return elementNameOrWildcard;
+ }
+
+ /**
+ * @return typeName
+ */
+ public NameTest getTypeName() {
+ return typeName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ElementTest) {
+ ElementTest that = (ElementTest)obj;
+ return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
+ && ObjectUtil.isEqualWithNulls(this.elementNameOrWildcard,
that.elementNameOrWildcard);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "element(" + elementNameOrWildcard + (typeName != null ?
"," + typeName : "") + ")";
+ }
+ }
+
+ public static class SchemaElementTest implements KindTest {
+ private final NameTest elementDeclarationName;
+
+ public SchemaElementTest( NameTest elementDeclarationName ) {
+ this.elementDeclarationName = elementDeclarationName;
+ }
+
+ /**
+ *@return the element declaration name, which will be a qualified name
+ */
+ public NameTest getElementDeclarationName() {
+ return elementDeclarationName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SchemaElementTest) {
+ SchemaElementTest that = (SchemaElementTest)obj;
+ return this.elementDeclarationName.equals(that.elementDeclarationName);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "schema-element(" + elementDeclarationName + ")";
+ }
+ }
+
+ public static class SchemaAttributeTest implements KindTest {
+ private final NameTest attributeDeclarationName;
+
+ public SchemaAttributeTest( NameTest attributeDeclarationName ) {
+ this.attributeDeclarationName = attributeDeclarationName;
+ }
+
+ /**
+ *@return the attribute declaration name, which will be a qualified name
+ */
+ public NameTest getAttributeDeclarationName() {
+ return attributeDeclarationName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SchemaAttributeTest) {
+ SchemaAttributeTest that = (SchemaAttributeTest)obj;
+ return
this.attributeDeclarationName.equals(that.attributeDeclarationName);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "schema-attribute(" + attributeDeclarationName +
")";
+ }
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
(rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java 2009-10-03
04:07:41 UTC (rev 1285)
@@ -0,0 +1,812 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import static org.jboss.dna.common.text.TokenStream.ANY_VALUE;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.common.CommonI18n;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.Position;
+import org.jboss.dna.common.text.TokenStream;
+import org.jboss.dna.common.text.TokenStream.CharacterStream;
+import org.jboss.dna.common.text.TokenStream.Tokenizer;
+import org.jboss.dna.common.text.TokenStream.Tokens;
+import org.jboss.dna.common.xml.XmlCharacters;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.QueryParser;
+import org.jboss.dna.jcr.xpath.XPath.Add;
+import org.jboss.dna.jcr.xpath.XPath.And;
+import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.AxisStep;
+import org.jboss.dna.jcr.xpath.XPath.CommentTest;
+import org.jboss.dna.jcr.xpath.XPath.Comparison;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.jboss.dna.jcr.xpath.XPath.ContextItem;
+import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
+import org.jboss.dna.jcr.xpath.XPath.DocumentTest;
+import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.FilterStep;
+import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.KindTest;
+import org.jboss.dna.jcr.xpath.XPath.Literal;
+import org.jboss.dna.jcr.xpath.XPath.NameTest;
+import org.jboss.dna.jcr.xpath.XPath.Negation;
+import org.jboss.dna.jcr.xpath.XPath.NodeComparison;
+import org.jboss.dna.jcr.xpath.XPath.NodeComparisonOperator;
+import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.Or;
+import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
+import org.jboss.dna.jcr.xpath.XPath.PathExpression;
+import org.jboss.dna.jcr.xpath.XPath.ProcessingInstructionTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaAttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaElementTest;
+import org.jboss.dna.jcr.xpath.XPath.StepExpression;
+import org.jboss.dna.jcr.xpath.XPath.Subtract;
+import org.jboss.dna.jcr.xpath.XPath.TextTest;
+import org.jboss.dna.jcr.xpath.XPath.Union;
+
+/**
+ *
+ */
+public class XPathParser implements QueryParser {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String,
org.jboss.dna.graph.ExecutionContext)
+ */
+ public QueryCommand parseQuery( String query,
+ ExecutionContext context ) {
+ Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
+ TokenStream tokens = new TokenStream(query, tokenizer, true).start(); // case
sensitive!!
+ Component result = new Parser(context).parseXPath(tokens);
+ System.out.println(query);
+ System.out.println(" --> " + result);
+ return null;
+ }
+
+ protected static class Parser {
+ private final ExecutionContext context;
+
+ protected Parser( ExecutionContext context ) {
+ this.context = context;
+ }
+
+ protected Component parseXPath( TokenStream tokens ) {
+ return parseExpr(tokens);
+ }
+
+ protected Component parseExpr( TokenStream tokens ) {
+ Component result = parseExprSingle(tokens);
+ if (tokens.matches(',')) {
+ throw new ParsingException(tokens.nextPosition(), "Multiple XPath
expressions are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseExprSingle( TokenStream tokens ) {
+ if (tokens.matches("for", "$", ANY_VALUE,
"IN")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'for' expressions are not supported");
+ }
+ if (tokens.matches("some", "$", ANY_VALUE,
"IN")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'some' expressions are not supported");
+ }
+ if (tokens.matches("every", "$", ANY_VALUE,
"IN")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'every' expressions are not supported");
+ }
+ if (tokens.matches("if", "(", ANY_VALUE, "IN"))
{
+ throw new ParsingException(tokens.nextPosition(), "XPath
if-then-else expressions are not supported");
+ }
+ return parseOrExpr(tokens);
+ }
+
+ protected Component parseOrExpr( TokenStream tokens ) {
+ Component result = parseAndExpr(tokens);
+ while (tokens.canConsume("or")) {
+ result = new Or(result, parseInstanceofExpr(tokens));
+ }
+ return result;
+ }
+
+ protected Component parseAndExpr( TokenStream tokens ) {
+ Component result = parseInstanceofExpr(tokens);
+ while (tokens.canConsume("and")) {
+ result = new And(result, parseInstanceofExpr(tokens));
+ }
+ return result;
+ }
+
+ protected Component parseInstanceofExpr( TokenStream tokens ) {
+ Component result = parseTreatExpr(tokens);
+ if (tokens.matches("instance", "of")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'instance of' expressions are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseTreatExpr( TokenStream tokens ) {
+ Component result = parseCastableExpr(tokens);
+ if (tokens.matches("treat", "as")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath 'treat
as' expressions are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseCastableExpr( TokenStream tokens ) {
+ Component result = parseCastExpr(tokens);
+ if (tokens.matches("castable", "as")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'castable as' expressions are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseCastExpr( TokenStream tokens ) {
+ Component result = parseComparisonExpr(tokens);
+ if (tokens.matches("cast", "as")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath 'cast
as' expressions are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseComparisonExpr( TokenStream tokens ) {
+ Component result = parseRangeExpr(tokens);
+ // General comparison is optional ...
+ Operator operator = parseGeneralComp(tokens);
+ if (operator == null) parseValueComp(tokens);
+ if (operator != null) {
+ return new Comparison(result, operator, parseRangeExpr(tokens));
+ }
+ NodeComparisonOperator nodeComp = parseNodeComp(tokens);
+ if (nodeComp != null) {
+ return new NodeComparison(result, nodeComp, parseRangeExpr(tokens));
+ }
+ return result;
+ }
+
+ protected Component parseValueComp( TokenStream tokens ) {
+ if (tokens.matchesAnyOf("eq", "ne", "lt",
"le", "gt")) {
+ throw new ParsingException(tokens.nextPosition(),
+ "XPath value comparisons using
'eq', 'ne', 'lt', 'le', or 'gt' are not
supported");
+ }
+ return null;
+ }
+
+ protected NodeComparisonOperator parseNodeComp( TokenStream tokens ) {
+ if (tokens.matches("is") || tokens.matches("<",
"<") || tokens.matches(">", ">")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath
'is', '<<' and '>>' expressions are not
supported");
+ }
+ return null;
+ }
+
+ protected Component parseRangeExpr( TokenStream tokens ) {
+ Component result = parseAdditiveExpr(tokens);
+ if (tokens.matches("to")) {
+ throw new ParsingException(tokens.nextPosition(), "XPath range
expressions with 'to' are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseAdditiveExpr( TokenStream tokens ) {
+ Component result = parseMultiplicativeExpr(tokens);
+ while (true) {
+ if (tokens.canConsume("+")) {
+ result = new Add(result, parseMultiplicativeExpr(tokens));
+ } else if (tokens.canConsume("-")) {
+ result = new Subtract(result, parseMultiplicativeExpr(tokens));
+ } else {
+ break; // no more additions
+ }
+ }
+ return result;
+ }
+
+ protected Component parseMultiplicativeExpr( TokenStream tokens ) {
+ Component result = parseUnaryExpr(tokens);
+ if (tokens.matchesAnyOf("+", "div", "idiv",
"mod")) {
+ throw new ParsingException(tokens.nextPosition(),
+ "XPath multiplicative expressions using
'+', 'div', 'idiv', or 'mod' are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseUnaryExpr( TokenStream tokens ) {
+ boolean negative = false;
+ // Technically more than one +/- are allowed by the spec
+ while (tokens.matchesAnyOf("+", "-")) {
+ if (tokens.canConsume("-")) negative = true;
+ tokens.canConsume("+");
+ }
+ Component result = parseUnionExpr(tokens);
+ return negative ? new Negation(result) : result;
+ }
+
+ protected Component parseUnionExpr( TokenStream tokens ) {
+ Component result = parseIntersectExceptExpr(tokens);
+ while (true) {
+ if (tokens.canConsumeAnyOf("union", "|")) {
+ result = new Union(result, parseIntersectExceptExpr(tokens));
+ } else {
+ break; // no more
+ }
+ }
+ return result;
+ }
+
+ protected Component parseIntersectExceptExpr( TokenStream tokens ) {
+ Component result = parseValueExpr(tokens);
+ if (tokens.matchesAnyOf("intersect", "except")) {
+ throw new ParsingException(tokens.nextPosition(),
+ "XPath multiplicative expressions using
'+', 'div', 'idiv', or 'mod' are not supported");
+ }
+ return result;
+ }
+
+ protected Component parseValueExpr( TokenStream tokens ) {
+ return parsePathExpr(tokens);
+ }
+
+ protected PathExpression parsePathExpr( TokenStream tokens ) {
+ boolean relative = true;
+ boolean prependDependentOrSelf = false;
+ if (tokens.canConsume('/')) {
+ if (tokens.canConsume('/')) {
+ if (!tokens.hasNext()) {
+ // See
http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
+ throw new ParsingException(tokens.previousPosition(),
"'//' is not a valid XPath expression");
+ }
+ prependDependentOrSelf = true;
+ }
+ relative = false;
+ }
+ PathExpression result = new PathExpression(relative,
parseRelativePathExpr(tokens).getSteps());
+ if (prependDependentOrSelf) {
+ result.getSteps().add(0, new DescendantOrSelf());
+ }
+ return result;
+ }
+
+ protected PathExpression parseRelativePathExpr( TokenStream tokens ) {
+ List<StepExpression> steps = new ArrayList<StepExpression>();
+ steps.add(parseStepExpr(tokens));
+ while (tokens.canConsume('/')) {
+ if (tokens.canConsume('/')) {
+ steps.add(new DescendantOrSelf());
+ }
+ if (tokens.hasNext()) {
+ steps.add(parseStepExpr(tokens));
+ }
+ }
+ return new PathExpression(true, steps);
+ }
+
+ protected StepExpression parseStepExpr( TokenStream tokens ) {
+ if (tokens.matches('(') || tokens.matches('.') ||
tokens.matches(XPathTokenizer.QUOTED_STRING)
+ || tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE,
":", ANY_VALUE, "(")) {
+ // We know its a filter expression (though literals don't fit this
pattern) ...
+ return parseFilterExpr(tokens);
+ }
+ AxisStep result = parseAxisStep(tokens);
+ if (result != null) return result;
+ // It must be the remaining kind of filter expression ...
+ return parseFilterExpr(tokens);
+ }
+
+ protected AxisStep parseAxisStep( TokenStream tokens ) {
+ NodeTest nodeTest = null;
+ if (tokens.canConsume('@')) {
+ // Abbreviated forward step with an attribute...
+ nodeTest = new AttributeNameTest(parseNodeTest(tokens));
+ } else if (tokens.matches('*')) {
+ // Abbreviated forward step with an wildcard element ...
+ nodeTest = parseNodeTest(tokens);
+
+ } else if (tokens.matches("child", ":", ":") ||
tokens.matches("attribute", ":", ":")
+ || tokens.matches("self", ":", ":")
|| tokens.matches("descendant", ":", ":")
+ || tokens.matches("descendant-or-self", ":",
":") || tokens.matches("following-sibling", ":",
":")
+ || tokens.matches("following", ":",
":") || tokens.matches("namespace", ":", ":")) {
+ // No non-abbreviated forward steps allowed
+ throw new ParsingException(
+ tokens.nextPosition(),
+ "XPath non-abbreviated forward steps
(e.g., 'child::', 'attribute::', 'self::', 'descendant::',
'descendant-or-self::', 'following-sibling::', 'following::', or
'namespace::') are not supported");
+ } else if (tokens.matches("..")) {
+ // No abbreviated reverse steps allowed ...
+ throw new ParsingException(tokens.nextPosition(),
+ "XPath abbreviated reverse steps (e.g.,
'..') are not supported");
+ } else if (tokens.matches("parent", ":", ":")
|| tokens.matches("ancestor-or-self", ":", ":")
+ || tokens.matches("preceding-sibling", ":",
":") || tokens.matches("preceding", ":", ":")
+ || tokens.matches("ancestor", ":",
":")) {
+ // No non-abbreviated reverse steps allowed ...
+ throw new ParsingException(
+ tokens.nextPosition(),
+ "XPath non-abbreviated reverse steps
(e.g., 'parent::', 'ancestor::', 'ancestor-or-self::',
'preceding-or-sibling::', or 'preceding::') are not supported");
+ } else if (tokens.matches(ANY_VALUE, ":", ANY_VALUE)
+ && tokens.matches(XPathTokenizer.NAME,
XPathTokenizer.SYMBOL, XPathTokenizer.NAME)) {
+ // This is probably a forward step with a (qualified) name test ...
+ nodeTest = parseQName(tokens);
+ } else if (tokens.matches(XPathTokenizer.NAME)) {
+ // This is probably a forward step with an unqualified name test ...
+ nodeTest = parseNodeTest(tokens);
+ } else {
+ return null;
+ }
+
+ // Parse the predicates
+ List<Component> predicates = parsePredicates(tokens);
+ return new AxisStep(nodeTest, predicates);
+ }
+
+ protected List<Component> parsePredicates( TokenStream tokens ) {
+ List<Component> predicates = new ArrayList<Component>();
+ while (tokens.canConsume('[')) {
+ predicates.add(parseExpr(tokens));
+ tokens.consume(']');
+ }
+ return predicates;
+ }
+
+ protected FilterStep parseFilterExpr( TokenStream tokens ) {
+ Component primaryExpr = parsePrimaryExpr(tokens);
+ List<Component> predicates = parsePredicates(tokens);
+ return new FilterStep(primaryExpr, predicates);
+ }
+
+ protected Component parsePrimaryExpr( TokenStream tokens ) {
+ if (tokens.matches('(')) {
+ return parseParenthesizedExpr(tokens);
+ }
+ if (tokens.matches('.')) {
+ return parseContextItemExpr(tokens);
+ }
+ if (tokens.matches(XPathTokenizer.QUOTED_STRING)) {
+ return parseStringLiteral(tokens);
+ }
+ if (tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE,
":", ANY_VALUE, "(")) {
+ return parseFunctionCall(tokens);
+ }
+ return parseNumericLiteral(tokens);
+ }
+
+ protected ContextItem parseContextItemExpr( TokenStream tokens ) {
+ tokens.consume('.');
+ return new ContextItem();
+ }
+
+ protected ParenthesizedExpression parseParenthesizedExpr( TokenStream tokens ) {
+ tokens.consume('(');
+ if (tokens.canConsume(')')) {
+ return new ParenthesizedExpression();
+ }
+ Component expr = parseExpr(tokens);
+ tokens.consume(')');
+ return new ParenthesizedExpression(expr);
+ }
+
+ protected Literal parseNumericLiteral( TokenStream tokens ) {
+ Position pos = tokens.nextPosition();
+ String sign = "";
+ if (tokens.canConsume('-')) sign = "-";
+ else if (tokens.canConsume('+')) sign = "";
+
+ // Try to parse this value as a number ...
+ ValueFactory<String> stringFactory =
context.getValueFactories().getStringFactory();
+ String number = tokens.consume();
+ if (number.indexOf(".") != -1) {
+ String value = sign + number;
+ if (value.endsWith("e") && (tokens.matches('+')
|| tokens.matches('-'))) {
+ // There's more to the number ...
+ value = value + tokens.consume() + tokens.consume(); // +/-EXP
+ }
+ try {
+ // Convert to a double and then back to a string to get canonical
form ...
+ String canonical =
stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
+ return new Literal(canonical);
+ } catch (ValueFormatException e) {
+ String msg =
GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(),
pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ // try to parse an a long ...
+ String value = sign + number;
+ try {
+ // Convert to a long and then back to a string to get canonical form ...
+ String canonical =
stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+ return new Literal(canonical);
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value,
pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+
+ protected Literal parseStringLiteral( TokenStream tokens ) {
+ boolean removeQuotes = tokens.matches(XPathTokenizer.QUOTED_STRING);
+ String value = tokens.consume();
+ if (removeQuotes) value = removeQuotes(value);
+ return new Literal(value);
+ }
+
+ protected FunctionCall parseFunctionCall( TokenStream tokens ) {
+ NameTest name = parseQName(tokens);
+ tokens.consume("(");
+ List<Component> args = new ArrayList<Component>();
+ if (!tokens.matches(')')) {
+ do {
+ args.add(parseExprSingle(tokens));
+ } while (tokens.canConsume(","));
+ tokens.consume(")");
+ }
+ return new FunctionCall(name, args);
+ }
+
+ protected Operator parseGeneralComp( TokenStream tokens ) {
+ if (tokens.canConsume("!", "=")) return
Operator.NOT_EQUAL_TO;
+ if (tokens.canConsume("=")) return Operator.EQUAL_TO;
+ if (tokens.canConsume("<", "=")) return
Operator.LESS_THAN_OR_EQUAL_TO;
+ if (tokens.canConsume(">", "=")) return
Operator.GREATER_THAN_OR_EQUAL_TO;
+ if (tokens.canConsume("<")) return Operator.LESS_THAN;
+ if (tokens.canConsume(">")) return Operator.GREATER_THAN;
+ return null;
+ }
+
+ protected NodeTest parseNodeTest( TokenStream tokens ) {
+ KindTest kind = parseKindTest(tokens);
+ if (kind != null) return kind;
+ return parseNameTest(tokens);
+ }
+
+ protected NameTest parseNameTest( TokenStream tokens ) {
+ NameTest wildcard = parseWildcard(tokens);
+ if (wildcard != null) return wildcard;
+ return parseQName(tokens);
+ }
+
+ protected NameTest parseQName( TokenStream tokens ) {
+ String firstPart = parseNCName(tokens);
+ if (tokens.canConsume(':')) {
+ String secondPart = tokens.consume();
+ return new NameTest(firstPart, secondPart);
+ }
+ return new NameTest(null, firstPart);
+ }
+
+ protected String parseNCName( TokenStream tokens ) {
+ String name = tokens.consume();
+ if (!XmlCharacters.isValidNcName(name)) {
+ throw new ParsingException(tokens.previousPosition(), "Expected
valid NCName but found " + name);
+ }
+ return name;
+ }
+
+ protected NameTest parseWildcard( TokenStream tokens ) {
+ if (tokens.canConsume('*')) {
+ if (tokens.canConsume(':')) {
+ if (tokens.canConsume('*')) {
+ return new NameTest(null, null);
+ }
+ String localName = tokens.consume();
+ return new NameTest(null, localName);
+ }
+ return new NameTest(null, null);
+ }
+ if (tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL,
XPathTokenizer.SYMBOL)
+ && tokens.matches(TokenStream.ANY_VALUE, ":",
"*")) {
+ String prefix = tokens.consume();
+ tokens.consume(':');
+ tokens.consume('*');
+ return new NameTest(prefix, null);
+ }
+ return null;
+ }
+
+ protected NameTest parseItemType( TokenStream tokens ) {
+ return parseQName(tokens);
+ }
+
+ protected NameTest parseAtomicType( TokenStream tokens ) {
+ return parseQName(tokens);
+ }
+
+ protected KindTest parseKindTest( TokenStream tokens ) {
+ KindTest result = parseAnyKindTest(tokens);
+ if (result == null) result = parseDocumentTest(tokens);
+ if (result == null) result = parseElementTest(tokens);
+ if (result == null) result = parseAttributeTest(tokens);
+ if (result == null) result = parseSchemaElementTest(tokens);
+ if (result == null) result = parseSchemaAttributeTest(tokens);
+ if (result == null) result = parsePITest(tokens);
+ if (result == null) result = parseCommentTest(tokens);
+ if (result == null) result = parseTextTest(tokens);
+ return result;
+ }
+
+ protected AnyKindTest parseAnyKindTest( TokenStream tokens ) {
+ if (tokens.canConsume("node", "(", ")")) {
+ return new AnyKindTest();
+ }
+ return null;
+ }
+
+ protected ProcessingInstructionTest parsePITest( TokenStream tokens ) {
+ if (tokens.canConsume("processing-instruction", "(")) {
+ if (tokens.canConsume(")")) return new
ProcessingInstructionTest(null);
+ String nameOrStringLiteral = tokens.consume();
+ tokens.consume(")");
+ return new ProcessingInstructionTest(nameOrStringLiteral);
+ }
+ return null;
+ }
+
+ protected CommentTest parseCommentTest( TokenStream tokens ) {
+ if (tokens.canConsume("comment", "(", ")")) {
+ return new CommentTest();
+ }
+ return null;
+ }
+
+ protected TextTest parseTextTest( TokenStream tokens ) {
+ if (tokens.canConsume("text", "(", ")")) {
+ return new TextTest();
+ }
+ return null;
+ }
+
+ protected DocumentTest parseDocumentTest( TokenStream tokens ) {
+ if (tokens.canConsume("document-node", "(")) {
+ // Document test ...
+ ElementTest elementTest = parseElementTest(tokens);
+ DocumentTest result = null;
+ if (elementTest != null) {
+ result = new DocumentTest(elementTest);
+ } else {
+ SchemaElementTest schemaTest = parseSchemaElementTest(tokens);
+ result = schemaTest != null ? new DocumentTest(schemaTest) : null;
+ }
+ tokens.consume(")");
+ return result;
+ }
+ return null;
+ }
+
+ protected ElementTest parseElementTest( TokenStream tokens ) {
+ if (tokens.canConsume("element", "(")) {
+ if (tokens.canConsume(")") || tokens.canConsume("*",
")")) {
+ return new ElementTest(new NameTest(null, null), new NameTest(null,
null));
+ }
+ ElementTest result = null;
+ NameTest elementName = parseNameTest(tokens);
+ if (tokens.canConsume(",")) {
+ NameTest typeName = parseNameTest(tokens);
+ result = new ElementTest(elementName, typeName);
+ } else {
+ result = new ElementTest(elementName, new NameTest(null, null));
+ }
+ tokens.consume(")");
+ return result;
+ }
+ return null;
+ }
+
+ protected SchemaElementTest parseSchemaElementTest( TokenStream tokens ) {
+ if (tokens.canConsume("schema-element", "(")) {
+ NameTest elementDeclarationName = parseNameTest(tokens);
+ SchemaElementTest result = new
SchemaElementTest(elementDeclarationName);
+ tokens.consume(")");
+ return result;
+ }
+ return null;
+ }
+
+ protected AttributeTest parseAttributeTest( TokenStream tokens ) {
+ if (tokens.canConsume("attribute", "(")) {
+ if (tokens.canConsume(")") || tokens.canConsume("*",
")")) {
+ return new AttributeTest(new NameTest(null, null), new NameTest(null,
null));
+ }
+ AttributeTest result = null;
+ NameTest attributeName = parseNameTest(tokens);
+ if (tokens.canConsume(",")) {
+ NameTest typeName = parseNameTest(tokens);
+ result = new AttributeTest(attributeName, typeName);
+ } else {
+ result = new AttributeTest(attributeName, new NameTest(null, null));
+ }
+ tokens.consume(")");
+ return result;
+ }
+ return null;
+ }
+
+ protected SchemaAttributeTest parseSchemaAttributeTest( TokenStream tokens ) {
+ if (tokens.canConsume("schema-attribute", "(")) {
+ NameTest attributeDeclarationName = parseNameTest(tokens);
+ SchemaAttributeTest result = new
SchemaAttributeTest(attributeDeclarationName);
+ tokens.consume(")");
+ return result;
+ }
+ return null;
+ }
+
+ protected void parseSingleType( TokenStream tokens ) {
+ }
+
+ protected void parseSequenceType( TokenStream tokens ) {
+ }
+
+ /**
+ * Remove any leading and trailing single-quotes or double-quotes from the
supplied text.
+ *
+ * @param text the input text; may not be null
+ * @return the text without leading and trailing quotes, or
<code>text</code> if there were no square brackets or quotes
+ */
+ protected String removeQuotes( String text ) {
+ return text.replaceFirst("^['\"]+",
"").replaceAll("['\"]+$", "");
+ }
+ }
+
+ /**
+ * A {@link TokenStream.Tokenizer} implementation that parses single- and
double-quoted strings, symbols, words consisting of
+ * {@link TokenStream.CharacterStream#isNextValidXmlNcNameCharacter() NCName}s (as
defined by the <a
+ *
href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">Namespaces in
XML 1.0</a> specification), XPath comments,and
+ * {@link #OTHER other} single-character tokens. Quoted phrases are delimited by
single-quote and double-quote characters
+ * (which may be escaped within the quote). XPath comments begin with a
"(:" and end with a ":)".
+ */
+ public static class XPathTokenizer implements TokenStream.Tokenizer {
+ /**
+ * The token type for tokens that represent an unquoted string containing a
character sequence made up of non-whitespace
+ * and non-symbol characters.
+ */
+ public static final int NAME = 2 << 0;
+ /**
+ * The token type for tokens that consist of an individual "symbol"
character. The set of characters includes:
+ * <code>(){}*.,;+%?$!<>|=:-[]^/\#@</code>
+ */
+ public static final int SYMBOL = 2 << 1;
+ /**
+ * The token type for tokens that consist of all the characters within
single-quotes, double-quotes, or square brackets.
+ */
+ public static final int QUOTED_STRING = 2 << 2;
+ /**
+ * The token type for tokens that consist of all the characters between
"(:" and ":)".
+ */
+ public static final int COMMENT = 2 << 3;
+ /**
+ * The token type for tokens that consist of single characters that are not a
{@link #SYMBOL}, valid {@link #NAME}, or
+ * {@link #QUOTED_STRING}.
+ */
+ public static final int OTHER = 2 << 4;
+
+ private final boolean useComments;
+
+ public XPathTokenizer( boolean useComments ) {
+ this.useComments = useComments;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.common.text.TokenStream.Tokenizer#tokenize(CharacterStream,
Tokens)
+ */
+ public void tokenize( CharacterStream input,
+ Tokens tokens ) throws ParsingException {
+ while (input.hasNext()) {
+ char c = input.next();
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ // Just skip these whitespace characters ...
+ break;
+ case ')':
+ case '{':
+ case '}':
+ case '*':
+ case '.':
+ case ',':
+ case ';':
+ case '+':
+ case '%':
+ case '?':
+ case '$':
+ case '!':
+ case '<':
+ case '>':
+ case '|':
+ case '=':
+ case ':':
+ case '-':
+ case '[':
+ case ']':
+ case '^':
+ case '/':
+ case '\\':
+ case '#':
+ case '@':
+ tokens.addToken(input.position(), input.index(), input.index() +
1, SYMBOL);
+ break;
+ case '\'':
+ case '\"':
+ int startIndex = input.index();
+ char closingChar = c;
+ Position pos = input.position();
+ boolean foundClosingQuote = false;
+ while (input.hasNext()) {
+ c = input.next();
+ if (c == closingChar && input.isNext(closingChar)) {
+ c = input.next(); // consume the next closeChar since it
is escaped
+ } else if (c == closingChar) {
+ foundClosingQuote = true;
+ break;
+ }
+ }
+ if (!foundClosingQuote) {
+ String msg =
CommonI18n.noMatchingDoubleQuoteFound.text(pos.getLine(), pos.getColumn());
+ if (closingChar == '\'') {
+ msg =
CommonI18n.noMatchingSingleQuoteFound.text(pos.getLine(), pos.getColumn());
+ }
+ throw new ParsingException(pos, msg);
+ }
+ int endIndex = input.index() + 1; // beyond last character read
+ tokens.addToken(pos, startIndex, endIndex, QUOTED_STRING);
+ break;
+ case '(':
+ startIndex = input.index();
+ if (input.isNext(':')) {
+ // This is a comment ...
+ pos = input.position();
+ while (input.hasNext() && !input.isNext(':',
')')) {
+ c = input.next();
+ }
+ if (input.hasNext()) input.next(); // consume the
':'
+ if (input.hasNext()) input.next(); // consume the
')'
+ if (useComments) {
+ endIndex = input.index() + 1; // the token will include
the closing ':' and ')' characters
+ tokens.addToken(pos, startIndex, endIndex, COMMENT);
+ }
+ } else {
+ tokens.addToken(input.position(), input.index(),
input.index() + 1, SYMBOL);
+ break;
+ }
+ break;
+ default:
+ startIndex = input.index();
+ pos = input.position();
+ // Read as long as there is a valid XML character ...
+ int tokenType = (XmlCharacters.isValidNcNameStart(c)) ? NAME :
OTHER;
+ while (input.isNextValidXmlNcNameCharacter()) {
+ c = input.next();
+ }
+ endIndex = input.index() + 1; // beyond last character that was
included
+ tokens.addToken(pos, startIndex, endIndex, tokenType);
+ }
+ }
+ }
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
(rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java 2009-10-03
04:07:41 UTC (rev 1285)
@@ -0,0 +1,867 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Arrays;
+import java.util.List;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.TokenStream;
+import org.jboss.dna.common.text.TokenStream.Tokenizer;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.AxisStep;
+import org.jboss.dna.jcr.xpath.XPath.CommentTest;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.jboss.dna.jcr.xpath.XPath.ContextItem;
+import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
+import org.jboss.dna.jcr.xpath.XPath.DocumentTest;
+import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.FilterStep;
+import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.Literal;
+import org.jboss.dna.jcr.xpath.XPath.NameTest;
+import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
+import org.jboss.dna.jcr.xpath.XPath.PathExpression;
+import org.jboss.dna.jcr.xpath.XPath.SchemaAttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaElementTest;
+import org.jboss.dna.jcr.xpath.XPath.StepExpression;
+import org.jboss.dna.jcr.xpath.XPath.TextTest;
+import org.jboss.dna.jcr.xpath.XPathParser.XPathTokenizer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class XPathParserTest {
+
+ private ExecutionContext context;
+ private XPathParser.Parser parser;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ parser = new XPathParser.Parser(context);
+ }
+
+ @After
+ public void afterEach() {
+ parser = null;
+ }
+
+ @Test
+ public void shouldParseXPathExpressions() {
+ assertParsable("/jcr:root/a/b/c");
+ assertParsable("/jcr:root/a/b/c[*]");
+ assertParsable("/jcr:root/some[1]/element(nodes, my:type)[1]");
+ assertParsable("//element(*,my:type)");
+ assertParsable("//element(*,my:type)[@jcr:title='something' and
@globalProperty='something else']");
+ assertParsable("//element(*,my:type)[@jcr:title | @globalProperty]");
+ assertParsable("//element(*, my:type) order by @my:title");
+ assertParsable("//element(*, my:type) [jcr:contains(., 'jcr')] order
by jcr:score() descending");
+ assertParsable("//element(*, employee)[@secretary and @assistant]");
+ }
+
+ @Test
+ public void shouldParseXPathExpressionsThatCombineSeparateExpressions() {
+ assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e");
+ assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e or
/jcr:root/f/g/h");
+ }
+
+ @Test
+ public void shouldFailToParseInvalidXPathExpressions() {
+ assertNotParsable("//"); //
http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Path expression
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void
shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithNameTestAfter() {
+ assertThat(parser.parsePathExpr(tokenize("//b:c")),
is(pathExpr(descendantOrSelf(), axisStep(nameTest("b", "c")))));
+ }
+
+ @Test
+ public void
shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate() {
+ assertThat(parser.parsePathExpr(tokenize("//.[c]")),
is(pathExpr(descendantOrSelf(),
+
filterStep(contextItem(),
+
relativePathExpr(axisStep(nameTest("c")))))));
+ }
+
+ @Test
+ public void
shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate()
{
+ assertThat(parser.parsePathExpr(tokenize("//.[3]")),
is(pathExpr(descendantOrSelf(),
+
filterStep(contextItem(),
+
relativePathExpr(filterStep(literal("3")))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Relative path expression
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void
shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithNameTestAfter() {
+ assertThat(parser.parseRelativePathExpr(tokenize("a//b:c")),
is(relativePathExpr(axisStep(nameTest("a")),
+
descendantOrSelf(),
+
axisStep(nameTest("b", "c")))));
+ }
+
+ @Test
+ public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfAtEnd()
{
+ assertThat(parser.parseRelativePathExpr(tokenize("a//")),
+ is(relativePathExpr(axisStep(nameTest("a")),
descendantOrSelf())));
+ }
+
+ @Test
+ public void
shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate()
{
+ assertThat(parser.parseRelativePathExpr(tokenize("a//.[c]")),
+ is(relativePathExpr(axisStep(nameTest("a")),
+ descendantOrSelf(),
+ filterStep(contextItem(),
relativePathExpr(axisStep(nameTest("c")))))));
+ }
+
+ @Test
+ public void
shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate()
{
+ assertThat(parser.parseRelativePathExpr(tokenize("a//.[3]")),
+ is(relativePathExpr(axisStep(nameTest("a")),
+ descendantOrSelf(),
+ filterStep(contextItem(),
relativePathExpr(filterStep(literal("3")))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Step Expression
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseStepExpressionFromParenthesizedLiteral() {
+ assertThat(parser.parseStepExpr(tokenize("('foo')")),
+
is((StepExpression)filterStep(paren(relativePathExpr(filterStep(literal("foo")))))));
+ }
+
+ @Test
+ public void shouldParseStepExpressionFromQuotedStringLiteral() {
+ assertThat(parser.parseStepExpr(tokenize("'foo'")),
is((StepExpression)filterStep(literal("foo"))));
+ }
+
+ @Test
+ public void shouldParseStepExpressionFromFunctionCallWithUnqualifiedName() {
+ assertThat(parser.parseStepExpr(tokenize("element(*,*)")),
+
is((StepExpression)filterStep(functionCall(nameTest("element"),
+
relativePathExpr(axisStep(wildcard())),
+
relativePathExpr(axisStep(wildcard()))))));
+ }
+
+ @Test
+ public void shouldParseStepExpressionFromFunctionCallWithQualifiedName() {
+ assertThat(parser.parseStepExpr(tokenize("foo:bar(*)")),
+ is((StepExpression)filterStep(functionCall(nameTest("foo",
"bar"), relativePathExpr(axisStep(wildcard()))))));
+ }
+
+ @Test
+ public void shouldParseStepExpressionFromQualifiedNameWithPredicate() {
+ assertThat(parser.parseStepExpr(tokenize("foo:bar[3]")),
+ is((StepExpression)axisStep(nameTest("foo",
"bar"), relativePathExpr(filterStep(literal("3"))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Axis Step - attribute
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseAxisStepFromAttributeWithNoPrefix() {
+ assertThat(parser.parseAxisStep(tokenize("@foo")),
is(axisStep(attributeNameTest(nameTest("foo")))));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromAttributeWithPrefix() {
+ assertThat(parser.parseAxisStep(tokenize("@foo:bar")),
is(axisStep(attributeNameTest(nameTest("foo", "bar")))));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseAxisStepFromInvalidAttributeName() {
+ parser.parseAxisStep(tokenize("@3:invalidName"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Axis Step - name
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseAxisStepFromNameWithNoPrefix() {
+ assertThat(parser.parseAxisStep(tokenize("foo")),
is(axisStep(nameTest("foo"))));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromNameWithPrefix() {
+ assertThat(parser.parseAxisStep(tokenize("foo:bar")),
is(axisStep(nameTest("foo", "bar"))));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseAxisStepIfInvalidName() {
+ assertThat(parser.parseAxisStep(tokenize("3:invalidName")),
is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Axis Step - wildcard
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseAxisStepFromWildcard() {
+ assertThat(parser.parseAxisStep(tokenize("*")),
is(axisStep(wildcard())));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromWildcardPrefixAndNonWildcardLocalName() {
+ assertThat(parser.parseAxisStep(tokenize("*:foo")),
is(axisStep(nameTest(null, "foo"))));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromWithPrefixAndWildcardLocalName() {
+ assertThat(parser.parseAxisStep(tokenize("foo:*")),
is(axisStep(nameTest("foo", null))));
+ }
+
+ @Test
+ public void
shouldParseAxisStepFromWildcardPrefixAndNonWildcardLocalNameWithPredicates() {
+ assertThat(parser.parseAxisStep(tokenize("*:name[3]")),
is(axisStep(nameTest(null, "name"),
+
relativePathExpr(filterStep(literal("3"))))));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromWildcardLocalNameWithPredicates() {
+ assertThat(parser.parseAxisStep(tokenize("*[3]")),
is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+ }
+
+ @Test
+ public void shouldParseAxisStepFromWildcardPrefixAndLocalNameWithPredicates() {
+ assertThat(parser.parseAxisStep(tokenize("*:*[3]")),
is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Axis Step - self::, child::, descendant::, descendant-or-self::, following::,
following-sibling::, namespace::
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedSelfAxis() {
+ parser.parseAxisStep(tokenize("self::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedChildAxis() {
+ parser.parseAxisStep(tokenize("child::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedDescendantAxis() {
+ parser.parseAxisStep(tokenize("descendant::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedDescendantOrSelfAxis() {
+ parser.parseAxisStep(tokenize("descendant-or-self::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedFollowingAxis() {
+ parser.parseAxisStep(tokenize("following::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedFollowingSiblingAxis() {
+ parser.parseAxisStep(tokenize("following-sibling::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedNamespaceAxis() {
+ parser.parseAxisStep(tokenize("namespace::x"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Axis Step - parent::, ancestor::, ancestor-or-self::, preceding::,
preceding-sibling::
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedParentAxis() {
+ parser.parseAxisStep(tokenize("parent::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedAncestorAxis() {
+ parser.parseAxisStep(tokenize("ancestor::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedAncestorOrSelfAxis() {
+ parser.parseAxisStep(tokenize("ancestor-or-self::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedPrecedingAxis() {
+ parser.parseAxisStep(tokenize("preceding::x"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNonAbbreviatedPrecedingSiblingAxis() {
+ parser.parseAxisStep(tokenize("preceding-sibling::x"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Predicates
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParsePredicatesWhenThereIsOnlyOnePredicate() {
+ assertThat(parser.parsePredicates(tokenize("[foo]")),
is(predicates(relativePathExpr(axisStep(nameTest("foo"))))));
+ }
+
+ @Test
+ public void shouldParsePredicatesWhenThereAreMultiplePredicates() {
+
assertThat(parser.parsePredicates(tokenize("['foo']['bar']")),
+ is(predicates(relativePathExpr(filterStep(literal("foo"))),
relativePathExpr(filterStep(literal("bar"))))));
+ assertThat(parser.parsePredicates(tokenize("[foo][bar]")),
is(predicates(relativePathExpr(axisStep(nameTest("foo"))),
+
relativePathExpr(axisStep(nameTest("bar"))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Context item
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseContextItemExpr() {
+ assertThat(parser.parseContextItemExpr(tokenize(".")), is(new
ContextItem()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Parenthesized expression
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseParenthesizedExpression() {
+ assertThat(parser.parseParenthesizedExpr(tokenize("('foo')")),
is(paren(relativePathExpr(filterStep(literal("foo"))))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Number literal
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void
shouldParseNumberLiteralContainingUnquotedIntegralValuesIntoCanonicalStringRepresentation()
{
+ assertThat(parser.parseNumericLiteral(tokenize("1")),
is(literal("1")));
+ assertThat(parser.parseNumericLiteral(tokenize("-1")),
is(literal("-1")));
+ assertThat(parser.parseNumericLiteral(tokenize("0")),
is(literal("0")));
+ assertThat(parser.parseNumericLiteral(tokenize("+1001")),
is(literal("1001")));
+ }
+
+ @Test
+ public void
shouldParseNumberLiteralContainingUnquotedDecimalValuesIntoCanonicalStringRepresentation()
{
+ assertThat(parser.parseNumericLiteral(tokenize("1.2")),
is(literal("1.2")));
+ assertThat(parser.parseNumericLiteral(tokenize("-1.2")),
is(literal("-1.2")));
+ assertThat(parser.parseNumericLiteral(tokenize("0.2")),
is(literal("0.2")));
+ assertThat(parser.parseNumericLiteral(tokenize("+1001.2")),
is(literal("1001.2")));
+ assertThat(parser.parseNumericLiteral(tokenize("1.2e10")),
is(literal("1.2E10")));
+ assertThat(parser.parseNumericLiteral(tokenize("-1.2e10")),
is(literal("-1.2E10")));
+ assertThat(parser.parseNumericLiteral(tokenize("0.2e10")),
is(literal("2.0E9")));
+ assertThat(parser.parseNumericLiteral(tokenize("1.2e+10")),
is(literal("1.2E10")));
+ assertThat(parser.parseNumericLiteral(tokenize("-1.2e+10")),
is(literal("-1.2E10")));
+ assertThat(parser.parseNumericLiteral(tokenize("0.2e+10")),
is(literal("2.0E9")));
+ assertThat(parser.parseNumericLiteral(tokenize("1.2e-10")),
is(literal("1.2E-10")));
+ assertThat(parser.parseNumericLiteral(tokenize("-1.2e-10")),
is(literal("-1.2E-10")));
+ assertThat(parser.parseNumericLiteral(tokenize("0.2e-10")),
is(literal("2.0E-11")));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // String literal
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseStringLiteral() {
+ assertThat(parser.parseStringLiteral(tokenize("one")),
is(literal("one")));
+ assertThat(parser.parseStringLiteral(tokenize("'one'")),
is(literal("one")));
+ assertThat(parser.parseStringLiteral(tokenize("'one word as a
quote'")), is(literal("one word as a quote")));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Function call
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseFunctionCallWithZeroParameters() {
+ FunctionCall func = parser.parseFunctionCall(tokenize("a()"));
+ assertThat(func, is(notNullValue()));
+ assertThat(func.getName(), is(nameTest("a")));
+ assertThat(func.getParameters().isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldParseFunctionCallWithOneQuotedStringLiteralParameter() {
+ FunctionCall func =
parser.parseFunctionCall(tokenize("a('foo')"));
+ assertThat(func, is(notNullValue()));
+ assertThat(func.getName(), is(nameTest("a")));
+ assertThat(func.getParameters().size(), is(1));
+ assertThat(func.getParameters().get(0),
is((Component)relativePathExpr(filterStep(literal("foo")))));
+ }
+
+ @Test
+ public void shouldParseFunctionCallWithOneUnquotedStringLiteralParameter() {
+ FunctionCall func = parser.parseFunctionCall(tokenize("a(foo)"));
+ assertThat(func, is(notNullValue()));
+ assertThat(func.getName(), is(nameTest("a")));
+ assertThat(func.getParameters().size(), is(1));
+ assertThat(func.getParameters().get(0),
is((Component)relativePathExpr(axisStep(nameTest("foo")))));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // General comparison
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseGeneralComp() {
+ assertThat(parser.parseGeneralComp(tokenize("!=")),
is(Operator.NOT_EQUAL_TO));
+ assertThat(parser.parseGeneralComp(tokenize("=")),
is(Operator.EQUAL_TO));
+ assertThat(parser.parseGeneralComp(tokenize("<")),
is(Operator.LESS_THAN));
+ assertThat(parser.parseGeneralComp(tokenize("<=")),
is(Operator.LESS_THAN_OR_EQUAL_TO));
+ assertThat(parser.parseGeneralComp(tokenize(">")),
is(Operator.GREATER_THAN));
+ assertThat(parser.parseGeneralComp(tokenize(">=")),
is(Operator.GREATER_THAN_OR_EQUAL_TO));
+ assertThat(parser.parseGeneralComp(tokenize("<5")),
is(Operator.LESS_THAN));
+ assertThat(parser.parseGeneralComp(tokenize(">5")),
is(Operator.GREATER_THAN));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseGeneralCompIfOperatorPatternIsNotFound() {
+ assertThat(parser.parseGeneralComp(tokenize("name")),
is(nullValue()));
+ assertThat(parser.parseGeneralComp(tokenize("+")), is(nullValue()));
+ assertThat(parser.parseGeneralComp(tokenize("!+")), is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Name
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseName() {
+ assertThat(parser.parseNameTest(tokenize("*")), is(wildcard()));
+ assertThat(parser.parseNameTest(tokenize("*:*")), is(wildcard()));
+ assertThat(parser.parseNameTest(tokenize("*:name")), is(nameTest(null,
"name")));
+ assertThat(parser.parseNameTest(tokenize("name:*")),
is(nameTest("name", null)));
+ assertThat(parser.parseNameTest(tokenize("abc")),
is(nameTest("abc")));
+ assertThat(parser.parseNameTest(tokenize("abc:def")),
is(nameTest("abc", "def")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNameIfNotValidWildcardOrQName() {
+ parser.parseNameTest(tokenize("3ABC:def"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // QName
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseQName() {
+ assertThat(parser.parseQName(tokenize("abc")),
is(nameTest("abc")));
+ assertThat(parser.parseQName(tokenize("abc:def")),
is(nameTest("abc", "def")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQNameIfStartsWithDigit() {
+ parser.parseNCName(tokenize("3ABC:def"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // NCName
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseNCName() {
+ assertThat(parser.parseNCName(tokenize("ABCDEFGHIJKLMNOPQRSTUVWXYZ")),
is("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ assertThat(parser.parseNCName(tokenize("abcdefghijklmnopqrstuvwxyz")),
is("abcdefghijklmnopqrstuvwxyz"));
+ assertThat(parser.parseNCName(tokenize("a0123456789")),
is("a0123456789"));
+ assertThat(parser.parseNCName(tokenize("a_-3b")),
is("a_-3b"));
+ assertThat(parser.parseNCName(tokenize("abc:def")),
is("abc"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNCNameIfStartsWithDigit() {
+ parser.parseNCName(tokenize("3ABC"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // wildcard
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseWildcard() {
+ assertThat(parser.parseWildcard(tokenize("*")), is(wildcard()));
+ assertThat(parser.parseWildcard(tokenize("*:*")), is(wildcard()));
+ assertThat(parser.parseWildcard(tokenize("*:name")), is(nameTest(null,
"name")));
+ assertThat(parser.parseWildcard(tokenize("name:*")),
is(nameTest("name", null)));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseWildcardIfNotWildcard() {
+ assertThat(parser.parseWildcard(tokenize("name")), is(nullValue()));
+ assertThat(parser.parseWildcard(tokenize("name:foo")),
is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // kind test
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseKindTest() {
+ assertThat(parser.parseKindTest(tokenize("node()")),
is(instanceOf(AnyKindTest.class)));
+ assertThat(parser.parseKindTest(tokenize("comment()")),
is(instanceOf(CommentTest.class)));
+ assertThat(parser.parseKindTest(tokenize("text()")),
is(instanceOf(TextTest.class)));
+
assertThat(parser.parseKindTest(tokenize("document-node(element(foo:bar))")),
is(instanceOf(DocumentTest.class)));
+ assertThat(parser.parseKindTest(tokenize("element(foo:bar)")),
is(instanceOf(ElementTest.class)));
+ assertThat(parser.parseKindTest(tokenize("schema-element(foo:bar)")),
is(instanceOf(SchemaElementTest.class)));
+ assertThat(parser.parseKindTest(tokenize("attribute(foo:bar)")),
is(instanceOf(AttributeTest.class)));
+ assertThat(parser.parseKindTest(tokenize("schema-attribute(foo:bar)")),
is(instanceOf(SchemaAttributeTest.class)));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // node
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseAnyKindTest() {
+ assertThat(parser.parseAnyKindTest(tokenize("node()")),
is(notNullValue()));
+ }
+
+ @Test
+ public void
shouldReturnNullFromParseAnyKindTestIfNotFollowedByOpenAndCloseParenthesis() {
+ assertThat(parser.parseAnyKindTest(tokenize("node x )")),
is(nullValue()));
+ assertThat(parser.parseAnyKindTest(tokenize("node(x )")),
is(nullValue()));
+ assertThat(parser.parseAnyKindTest(tokenize("node(x")),
is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // comment
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseCommentTest() {
+ assertThat(parser.parseCommentTest(tokenize("comment()")),
is(notNullValue()));
+ }
+
+ @Test
+ public void
shouldReturnNullFromParseCommentTestIfNotFollowedByOpenAndCloseParenthesis() {
+ assertThat(parser.parseCommentTest(tokenize("comment x )")),
is(nullValue()));
+ assertThat(parser.parseCommentTest(tokenize("comment(x )")),
is(nullValue()));
+ assertThat(parser.parseCommentTest(tokenize("comment(x")),
is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // text
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseTextTest() {
+ assertThat(parser.parseTextTest(tokenize("text()")),
is(notNullValue()));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseTextTestIfNotFollowedByOpenAndCloseParenthesis()
{
+ assertThat(parser.parseTextTest(tokenize("text x )")),
is(nullValue()));
+ assertThat(parser.parseTextTest(tokenize("text(x )")),
is(nullValue()));
+ assertThat(parser.parseTextTest(tokenize("text(x")), is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // document-node
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDocumentTest() {
+ DocumentTest result =
parser.parseDocumentTest(tokenize("document-node(element(foo:bar))"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementTest().getElementName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getElementTest().getTypeName(), is(wildcard()));
+ assertThat(result.getSchemaElementTest(), is(nullValue()));
+
+ result =
parser.parseDocumentTest(tokenize("document-node(schema-element(foo))"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getSchemaElementTest().getElementDeclarationName(),
is(nameTest("foo")));
+ assertThat(result.getElementTest(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseDocumentTestIfOpenParenthesisIsNotIncluded() {
+ assertThat(parser.parseDocumentTest(tokenize("document-node foo")),
is(nullValue()));
+ assertThat(parser.parseDocumentTest(tokenize("document-node foo")),
is(nullValue()));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void
shouldFailToParseDocumentTestIfDocumentNodeDoesNotContainElementOrSchemaElement() {
+ parser.parseDocumentTest(tokenize("document-node(foo)"));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // element
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseElementTest() {
+ ElementTest result =
parser.parseElementTest(tokenize("element(foo)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(nameTest("foo")));
+ assertThat(result.getTypeName(), is(wildcard()));
+
+ result = parser.parseElementTest(tokenize("element(foo:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(wildcard()));
+
+ result = parser.parseElementTest(tokenize("element(foo:bar,baz)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(nameTest("baz")));
+
+ result =
parser.parseElementTest(tokenize("element(foo:bar,baz:bam)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(nameTest("baz",
"bam")));
+
+ result = parser.parseElementTest(tokenize("element(foo:bar,*)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(wildcard()));
+
+ result = parser.parseElementTest(tokenize("element(*,foo:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(wildcard()));
+ assertThat(result.getTypeName(), is(nameTest("foo",
"bar")));
+
+ result = parser.parseElementTest(tokenize("element(*,*)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementName(), is(wildcard()));
+ assertThat(result.getTypeName(), is(wildcard()));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseElementTestIfOpenParenthesisIsNotIncluded() {
+ ElementTest result = parser.parseElementTest(tokenize("attribute
foo"));
+ assertThat(result, is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // schema-element
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseSchemaElementTest() {
+ SchemaElementTest result =
parser.parseSchemaElementTest(tokenize("schema-element(foo)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementDeclarationName(), is(nameTest("foo")));
+
+ result =
parser.parseSchemaElementTest(tokenize("schema-element(foo:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementDeclarationName(), is(nameTest("foo",
"bar")));
+
+ result = parser.parseSchemaElementTest(tokenize("schema-element(*)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getElementDeclarationName(), is(wildcard()));
+ }
+
+ @Test
+ public void
shouldReturnNullFromParseSchemaElementTestIfOpenParenthesisIsNotIncluded() {
+ SchemaElementTest result =
parser.parseSchemaElementTest(tokenize("schema-element foo"));
+ assertThat(result, is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // attribute
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseAttributeTest() {
+ AttributeTest result =
parser.parseAttributeTest(tokenize("attribute(foo)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(nameTest("foo")));
+ assertThat(result.getTypeName(), is(wildcard()));
+
+ result = parser.parseAttributeTest(tokenize("attribute(foo:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(wildcard()));
+
+ result =
parser.parseAttributeTest(tokenize("attribute(foo:bar,baz)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(nameTest("baz")));
+
+ result =
parser.parseAttributeTest(tokenize("attribute(foo:bar,baz:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(nameTest("baz",
"bar")));
+
+ result = parser.parseAttributeTest(tokenize("attribute(*,baz:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(wildcard()));
+ assertThat(result.getTypeName(), is(nameTest("baz",
"bar")));
+
+ result = parser.parseAttributeTest(tokenize("attribute(foo:bar,*)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeName(), is(nameTest("foo",
"bar")));
+ assertThat(result.getTypeName(), is(wildcard()));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseAttributeTestIfOpenParenthesisIsNotIncluded() {
+ AttributeTest result = parser.parseAttributeTest(tokenize("attribute
foo"));
+ assertThat(result, is(nullValue()));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // schema-attribute
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseSchemaAttributeTest() {
+ SchemaAttributeTest result =
parser.parseSchemaAttributeTest(tokenize("schema-attribute(foo)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeDeclarationName(), is(nameTest("foo")));
+
+ result =
parser.parseSchemaAttributeTest(tokenize("schema-attribute(foo:bar)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeDeclarationName(), is(nameTest("foo",
"bar")));
+
+ result =
parser.parseSchemaAttributeTest(tokenize("schema-attribute(*)"));
+ assertThat(result, is(notNullValue()));
+ assertThat(result.getAttributeDeclarationName(), is(wildcard()));
+ }
+
+ @Test
+ public void
shouldReturnNullFromParseSchemaAttributeTestIfOpenParenthesisIsNotIncluded() {
+ SchemaAttributeTest result =
parser.parseSchemaAttributeTest(tokenize("schema-attribute foo"));
+ assertThat(result, is(nullValue()));
+ }
+
+ protected TokenStream tokenize( String xpath ) {
+ Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
+ return new TokenStream(xpath, tokenizer, true).start(); // case sensitive!!
+ }
+
+ protected DescendantOrSelf descendantOrSelf() {
+ return new DescendantOrSelf();
+ }
+
+ protected AxisStep axisStep( NodeTest nodeTest,
+ Component... predicates ) {
+ return new AxisStep(nodeTest, predicates(predicates));
+ }
+
+ protected FilterStep filterStep( Component primaryExpression,
+ Component... predicates ) {
+ return new FilterStep(primaryExpression, predicates(predicates));
+ }
+
+ protected PathExpression pathExpr( StepExpression... steps ) {
+ return pathExpr(Arrays.asList(steps));
+ }
+
+ protected PathExpression pathExpr( List<StepExpression> steps ) {
+ return new PathExpression(false, steps);
+ }
+
+ protected PathExpression relativePathExpr( StepExpression... steps ) {
+ return relativePathExpr(Arrays.asList(steps));
+ }
+
+ protected PathExpression relativePathExpr( List<StepExpression> steps ) {
+ return new PathExpression(true, steps);
+ }
+
+ protected Literal literal( String value ) {
+ return new Literal(value);
+ }
+
+ protected ContextItem contextItem() {
+ return new ContextItem();
+ }
+
+ protected List<Component> predicates( Component... predicates ) {
+ return Arrays.asList(predicates);
+ }
+
+ protected ParenthesizedExpression paren( Component value ) {
+ return new ParenthesizedExpression(value);
+ }
+
+ protected AttributeNameTest attributeNameTest( NameTest nameTest ) {
+ return new AttributeNameTest(nameTest);
+ }
+
+ protected AttributeTest attribute( NameTest name ) {
+ return new AttributeTest(name, null);
+ }
+
+ protected AttributeTest attribute( NameTest name,
+ NameTest type ) {
+ return new AttributeTest(name, type);
+ }
+
+ protected FunctionCall functionCall( NameTest name,
+ Component... parameters ) {
+ return new FunctionCall(name, Arrays.asList(parameters));
+ }
+
+ protected NameTest wildcard() {
+ return new NameTest(null, null);
+ }
+
+ protected NameTest nameTest( String localPart ) {
+ return new NameTest(null, localPart);
+ }
+
+ protected NameTest nameTest( String prefix,
+ String local ) {
+ return new NameTest(prefix, local);
+ }
+
+ protected void assertParsable( String xpath ) {
+ new XPathParser().parseQuery(xpath, context);
+ }
+
+ protected void assertNotParsable( String xpath ) {
+ try {
+ new XPathParser().parseQuery(xpath, context);
+ fail("Expected an invalid XPath: " + xpath);
+ } catch (ParsingException e) {
+ // expected
+ }
+ }
+
+}
Property changes on:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF