org.jaxen.saxpath.base.XPathReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaxen Show documentation
Show all versions of jaxen Show documentation
Jaxen is a universal Java XPath engine.
/*
* $Header$
* $Revision$
* $Date$
*
* ====================================================================
*
* Copyright 2000-2002 bob mcwhirter & James Strachan.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the Jaxen Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
* This software consists of voluntary contributions made by many
* individuals on behalf of the Jaxen Project and was originally
* created by bob mcwhirter and
* James Strachan . For more information on the
* Jaxen Project, please see .
*
* $Id$
*/
package org.jaxen.saxpath.base;
import java.util.ArrayList;
import org.jaxen.saxpath.Axis;
import org.jaxen.saxpath.Operator;
import org.jaxen.saxpath.SAXPathException;
import org.jaxen.saxpath.XPathHandler;
import org.jaxen.saxpath.XPathSyntaxException;
import org.jaxen.saxpath.helpers.DefaultXPathHandler;
/** Implementation of SAXPath's XPathReader
which
* generates callbacks to an XPathHandler
.
*
* @author bob mcwhirter ([email protected])
*/
public class XPathReader implements org.jaxen.saxpath.XPathReader
{
private ArrayList tokens;
private XPathLexer lexer;
private XPathHandler handler;
private static XPathHandler defaultHandler = new DefaultXPathHandler();
/**
* Create a new XPathReader
with a do-nothing
* XPathHandler
.
*/
public XPathReader()
{
setXPathHandler( defaultHandler );
}
public void setXPathHandler(XPathHandler handler)
{
this.handler = handler;
}
public XPathHandler getXPathHandler()
{
return this.handler;
}
public void parse(String xpath) throws SAXPathException
{
setUpParse( xpath );
getXPathHandler().startXPath();
expr();
getXPathHandler().endXPath();
if ( LA(1) != TokenTypes.EOF )
{
XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
throw ex;
}
lexer = null;
tokens = null;
}
void setUpParse(String xpath)
{
this.tokens = new ArrayList();
this.lexer = new XPathLexer( xpath );
}
private void pathExpr() throws SAXPathException
{
getXPathHandler().startPathExpr();
switch ( LA(1) )
{
case TokenTypes.DOUBLE:
case TokenTypes.LITERAL:
{
filterExpr();
if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH )
{
XPathSyntaxException ex = createSyntaxException("Node-set expected");
throw ex;
}
break;
}
case TokenTypes.LEFT_PAREN:
case TokenTypes.DOLLAR:
{
filterExpr();
if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
{
locationPath( false );
}
break;
}
case TokenTypes.IDENTIFIER:
{
if ( ( LA(2) == TokenTypes.LEFT_PAREN
&&
! isNodeTypeName( LT(1) ) )
||
( LA(2) == TokenTypes.COLON
&&
LA(4) == TokenTypes.LEFT_PAREN) )
{
filterExpr();
if ( LA(1) == TokenTypes.SLASH || LA(1) == TokenTypes.DOUBLE_SLASH)
{
locationPath( false );
}
}
else
{
locationPath( false );
}
break;
}
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.STAR:
case TokenTypes.AT:
{
locationPath( false );
break;
}
case TokenTypes.SLASH:
case TokenTypes.DOUBLE_SLASH:
{
locationPath( true );
break;
}
default:
{
XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
throw ex;
}
}
getXPathHandler().endPathExpr();
}
private void literal() throws SAXPathException
{
Token token = match( TokenTypes.LITERAL );
getXPathHandler().literal( token.getTokenText() );
}
private void functionCall() throws SAXPathException
{
String prefix = null;
String functionName = null;
if ( LA(2) == TokenTypes.COLON )
{
prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
match( TokenTypes.COLON );
}
else
{
prefix = "";
}
functionName = match( TokenTypes.IDENTIFIER ).getTokenText();
getXPathHandler().startFunction( prefix,
functionName );
match ( TokenTypes.LEFT_PAREN );
arguments();
match ( TokenTypes.RIGHT_PAREN );
getXPathHandler().endFunction();
}
private void arguments() throws SAXPathException
{
while ( LA(1) != TokenTypes.RIGHT_PAREN )
{
expr();
if ( LA(1) == TokenTypes.COMMA )
{
match( TokenTypes.COMMA );
}
else
{
break;
}
}
}
private void filterExpr() throws SAXPathException
{
getXPathHandler().startFilterExpr();
switch ( LA(1) )
{
case TokenTypes.DOUBLE:
{
Token token = match( TokenTypes.DOUBLE );
getXPathHandler().number( Double.parseDouble( token.getTokenText() ) );
break;
}
case TokenTypes.LITERAL:
{
literal();
break;
}
case TokenTypes.LEFT_PAREN:
{
match( TokenTypes.LEFT_PAREN );
expr();
match( TokenTypes.RIGHT_PAREN );
break;
}
case TokenTypes.IDENTIFIER:
{
functionCall();
break;
}
case TokenTypes.DOLLAR:
{
variableReference();
break;
}
}
predicates();
getXPathHandler().endFilterExpr();
}
private void variableReference() throws SAXPathException
{
match( TokenTypes.DOLLAR );
String prefix = null;
String variableName = null;
if ( LA(2) == TokenTypes.COLON )
{
prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
match( TokenTypes.COLON );
}
else
{
prefix = "";
}
variableName = match( TokenTypes.IDENTIFIER ).getTokenText();
getXPathHandler().variableReference( prefix,
variableName );
}
void locationPath(boolean isAbsolute) throws SAXPathException
{
switch ( LA(1) )
{
case TokenTypes.SLASH:
case TokenTypes.DOUBLE_SLASH:
{
if ( isAbsolute )
{
absoluteLocationPath();
}
else
{
relativeLocationPath();
}
break;
}
case TokenTypes.AT:
case TokenTypes.IDENTIFIER:
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.STAR:
{
relativeLocationPath();
break;
}
default:
{
XPathSyntaxException ex = createSyntaxException( "Unexpected '" + LT(1).getTokenText() + "'" );
throw ex;
}
}
}
private void absoluteLocationPath() throws SAXPathException
{
getXPathHandler().startAbsoluteLocationPath();
switch ( LA(1) )
{
case TokenTypes.SLASH:
{
match( TokenTypes.SLASH );
switch ( LA(1) )
{
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.AT:
case TokenTypes.IDENTIFIER:
case TokenTypes.STAR:
{
steps();
break;
}
}
break;
}
case TokenTypes.DOUBLE_SLASH:
{
getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
getXPathHandler().endAllNodeStep();
match( TokenTypes.DOUBLE_SLASH );
switch ( LA(1) )
{
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.AT:
case TokenTypes.IDENTIFIER:
case TokenTypes.STAR:
{
steps();
break;
}
default:
XPathSyntaxException ex = this.createSyntaxException("Location path cannot end with //");
throw ex;
}
break;
}
}
getXPathHandler().endAbsoluteLocationPath();
}
private void relativeLocationPath() throws SAXPathException
{
getXPathHandler().startRelativeLocationPath();
switch ( LA(1) )
{
case TokenTypes.SLASH:
{
match( TokenTypes.SLASH );
break;
}
case TokenTypes.DOUBLE_SLASH:
{
getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
getXPathHandler().endAllNodeStep();
match( TokenTypes.DOUBLE_SLASH );
break;
}
}
steps();
getXPathHandler().endRelativeLocationPath();
}
private void steps() throws SAXPathException
{
switch ( LA(1) )
{
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.AT:
case TokenTypes.IDENTIFIER:
case TokenTypes.STAR:
{
step();
break;
}
case TokenTypes.EOF:
{
return;
}
default:
{
XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', " );
throw ex;
}
}
do
{
if ( ( LA(1) == TokenTypes.SLASH)
||
( LA(1) == TokenTypes.DOUBLE_SLASH ) )
{
switch ( LA(1) )
{
case TokenTypes.SLASH:
{
match( TokenTypes.SLASH );
break;
}
case TokenTypes.DOUBLE_SLASH:
{
getXPathHandler().startAllNodeStep( Axis.DESCENDANT_OR_SELF );
getXPathHandler().endAllNodeStep();
match( TokenTypes.DOUBLE_SLASH );
break;
}
}
}
else
{
return;
}
switch ( LA(1) )
{
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
case TokenTypes.AT:
case TokenTypes.IDENTIFIER:
case TokenTypes.STAR:
{
step();
break;
}
default:
{
XPathSyntaxException ex = createSyntaxException( "Expected one of '.', '..', '@', '*', " );
throw ex;
}
}
} while ( true );
}
void step() throws SAXPathException
{
int axis = 0;
switch ( LA(1) )
{
case TokenTypes.DOT:
case TokenTypes.DOT_DOT:
{
abbrStep();
return;
}
case TokenTypes.AT:
{
axis = axisSpecifier();
break;
}
case TokenTypes.IDENTIFIER:
{
if ( LA(2) == TokenTypes.DOUBLE_COLON )
{
axis = axisSpecifier();
}
else
{
axis = Axis.CHILD;
}
break;
}
case TokenTypes.STAR:
{
axis = Axis.CHILD;
break;
}
}
nodeTest( axis );
}
private int axisSpecifier() throws SAXPathException
{
int axis = 0;
switch ( LA(1) )
{
case TokenTypes.AT:
{
match( TokenTypes.AT );
axis = Axis.ATTRIBUTE;
break;
}
case TokenTypes.IDENTIFIER:
{
Token token = LT( 1 );
axis = Axis.lookup( token.getTokenText() );
if ( axis == Axis.INVALID_AXIS )
{
throwInvalidAxis( token.getTokenText() );
}
match( TokenTypes.IDENTIFIER );
match( TokenTypes.DOUBLE_COLON );
break;
}
}
return axis;
}
private void nodeTest(int axis) throws SAXPathException
{
switch ( LA(1) )
{
case TokenTypes.IDENTIFIER:
{
switch ( LA(2) )
{
case TokenTypes.LEFT_PAREN:
{
nodeTypeTest( axis );
break;
}
default:
{
nameTest( axis );
break;
}
}
break;
}
case TokenTypes.STAR:
{
nameTest( axis );
break;
}
default:
XPathSyntaxException ex = createSyntaxException("Expected or *");
throw ex;
}
}
private void nodeTypeTest(int axis) throws SAXPathException
{
Token nodeTypeToken = match( TokenTypes.IDENTIFIER );
String nodeType = nodeTypeToken.getTokenText();
match( TokenTypes.LEFT_PAREN );
if ( "processing-instruction".equals( nodeType ) )
{
String piName = "";
if ( LA(1) == TokenTypes.LITERAL )
{
piName = match( TokenTypes.LITERAL ).getTokenText();
}
match( TokenTypes.RIGHT_PAREN );
getXPathHandler().startProcessingInstructionNodeStep( axis,
piName );
predicates();
getXPathHandler().endProcessingInstructionNodeStep();
}
else if ( "node".equals( nodeType ) )
{
match( TokenTypes.RIGHT_PAREN );
getXPathHandler().startAllNodeStep( axis );
predicates();
getXPathHandler().endAllNodeStep();
}
else if ( "text".equals( nodeType ) )
{
match( TokenTypes.RIGHT_PAREN );
getXPathHandler().startTextNodeStep( axis );
predicates();
getXPathHandler().endTextNodeStep();
}
else if ( "comment".equals( nodeType ) )
{
match( TokenTypes.RIGHT_PAREN );
getXPathHandler().startCommentNodeStep( axis );
predicates();
getXPathHandler().endCommentNodeStep();
}
else
{
XPathSyntaxException ex = createSyntaxException( "Expected node-type" );
throw ex;
}
}
private void nameTest(int axis) throws SAXPathException
{
String prefix = null;
String localName = null;
switch ( LA(2) )
{
case TokenTypes.COLON:
{
switch ( LA(1) )
{
case TokenTypes.IDENTIFIER:
{
prefix = match( TokenTypes.IDENTIFIER ).getTokenText();
match( TokenTypes.COLON );
break;
}
}
break;
}
}
switch ( LA(1) )
{
case TokenTypes.IDENTIFIER:
{
localName = match( TokenTypes.IDENTIFIER ).getTokenText();
break;
}
case TokenTypes.STAR:
{
match( TokenTypes.STAR );
localName = "*";
break;
}
}
if ( prefix == null )
{
prefix = "";
}
getXPathHandler().startNameStep( axis,
prefix,
localName );
predicates();
getXPathHandler().endNameStep();
}
private void abbrStep() throws SAXPathException
{
switch ( LA(1) )
{
case TokenTypes.DOT:
{
match( TokenTypes.DOT );
getXPathHandler().startAllNodeStep( Axis.SELF );
predicates();
getXPathHandler().endAllNodeStep();
break;
}
case TokenTypes.DOT_DOT:
{
match( TokenTypes.DOT_DOT );
getXPathHandler().startAllNodeStep( Axis.PARENT );
predicates();
getXPathHandler().endAllNodeStep();
break;
}
}
}
private void predicates() throws SAXPathException
{
while (true )
{
if ( LA(1) == TokenTypes.LEFT_BRACKET )
{
predicate();
}
else
{
break;
}
}
}
void predicate() throws SAXPathException
{
getXPathHandler().startPredicate();
match( TokenTypes.LEFT_BRACKET );
predicateExpr();
match( TokenTypes.RIGHT_BRACKET );
getXPathHandler().endPredicate();
}
private void predicateExpr() throws SAXPathException
{
expr();
}
private void expr() throws SAXPathException
{
orExpr();
}
private void orExpr() throws SAXPathException
{
getXPathHandler().startOrExpr();
andExpr();
boolean create = false;
switch ( LA(1) )
{
case TokenTypes.OR:
{
create = true;
match( TokenTypes.OR );
orExpr();
break;
}
}
getXPathHandler().endOrExpr( create );
}
private void andExpr() throws SAXPathException
{
getXPathHandler().startAndExpr();
equalityExpr();
boolean create = false;
switch ( LA(1) )
{
case TokenTypes.AND:
{
create = true;
match( TokenTypes.AND );
andExpr();
break;
}
}
getXPathHandler().endAndExpr( create );
}
private void equalityExpr() throws SAXPathException
{
relationalExpr();
int la = LA(1);
while (la == TokenTypes.EQUALS || la == TokenTypes.NOT_EQUALS)
{
switch ( la )
{
case TokenTypes.EQUALS:
{
match( TokenTypes.EQUALS );
getXPathHandler().startEqualityExpr();
relationalExpr();
getXPathHandler().endEqualityExpr( Operator.EQUALS );
break;
}
case TokenTypes.NOT_EQUALS:
{
match( TokenTypes.NOT_EQUALS );
getXPathHandler().startEqualityExpr();
relationalExpr();
getXPathHandler().endEqualityExpr( Operator.NOT_EQUALS );
break;
}
}
la = LA(1);
}
}
private void relationalExpr() throws SAXPathException
{
additiveExpr();
int la = LA(1);
// Very important: TokenTypes.LESS_THAN != Operator.LESS_THAN
// TokenTypes.GREATER_THAN != Operator.GREATER_THAN
// TokenTypes.GREATER_THAN_EQUALS != Operator.GREATER_THAN_EQUALS
// TokenTypes.LESS_THAN_EQUALS != Operator.LESS_THAN_EQUALS
while (la == TokenTypes.LESS_THAN_SIGN
|| la == TokenTypes.GREATER_THAN_SIGN
|| la == TokenTypes.LESS_THAN_OR_EQUALS_SIGN
|| la == TokenTypes.GREATER_THAN_OR_EQUALS_SIGN ) {
switch ( la )
{
case TokenTypes.LESS_THAN_SIGN:
{
match( TokenTypes.LESS_THAN_SIGN );
getXPathHandler().startRelationalExpr();
additiveExpr();
getXPathHandler().endRelationalExpr( Operator.LESS_THAN );
break;
}
case TokenTypes.GREATER_THAN_SIGN:
{
match( TokenTypes.GREATER_THAN_SIGN );
getXPathHandler().startRelationalExpr();
additiveExpr();
getXPathHandler().endRelationalExpr( Operator.GREATER_THAN );
break;
}
case TokenTypes.GREATER_THAN_OR_EQUALS_SIGN:
{
match( TokenTypes.GREATER_THAN_OR_EQUALS_SIGN );
getXPathHandler().startRelationalExpr();
additiveExpr();
getXPathHandler().endRelationalExpr( Operator.GREATER_THAN_EQUALS );
break;
}
case TokenTypes.LESS_THAN_OR_EQUALS_SIGN:
{
match( TokenTypes.LESS_THAN_OR_EQUALS_SIGN );
getXPathHandler().startRelationalExpr();
additiveExpr();
getXPathHandler().endRelationalExpr( Operator.LESS_THAN_EQUALS );
break;
}
}
la = LA(1);
}
}
private void additiveExpr() throws SAXPathException
{
multiplicativeExpr();
int la = LA(1);
while (la == TokenTypes.PLUS || la == TokenTypes.MINUS)
{
switch ( la )
{
case TokenTypes.PLUS:
{
match( TokenTypes.PLUS );
getXPathHandler().startAdditiveExpr();
multiplicativeExpr();
getXPathHandler().endAdditiveExpr( Operator.ADD );
break;
}
case TokenTypes.MINUS:
{
match( TokenTypes.MINUS );
getXPathHandler().startAdditiveExpr();
multiplicativeExpr();
getXPathHandler().endAdditiveExpr( Operator.SUBTRACT );
break;
}
}
la = LA(1);
}
}
private void multiplicativeExpr() throws SAXPathException
{
unaryExpr();
int la = LA(1);
while (la == TokenTypes.STAR_OPERATOR || la == TokenTypes.DIV || la == TokenTypes.MOD)
{
switch ( la )
{
case TokenTypes.STAR:
case TokenTypes.STAR_OPERATOR:
{
match( TokenTypes.STAR_OPERATOR );
getXPathHandler().startMultiplicativeExpr();
unaryExpr();
getXPathHandler().endMultiplicativeExpr( Operator.MULTIPLY );
break;
}
case TokenTypes.DIV:
{
match( TokenTypes.DIV );
getXPathHandler().startMultiplicativeExpr();
unaryExpr();
getXPathHandler().endMultiplicativeExpr( Operator.DIV );
break;
}
case TokenTypes.MOD:
{
match( TokenTypes.MOD );
getXPathHandler().startMultiplicativeExpr();
unaryExpr();
getXPathHandler().endMultiplicativeExpr( Operator.MOD );
break;
}
}
la = LA(1);
}
}
private void unaryExpr() throws SAXPathException
{
switch ( LA(1) )
{
case TokenTypes.MINUS:
{
getXPathHandler().startUnaryExpr();
match( TokenTypes.MINUS );
unaryExpr();
getXPathHandler().endUnaryExpr( Operator.NEGATIVE );
break;
}
default:
{
unionExpr();
break;
}
}
}
private void unionExpr() throws SAXPathException
{
getXPathHandler().startUnionExpr();
pathExpr();
boolean create = false;
switch ( LA(1) )
{
case TokenTypes.PIPE:
{
match( TokenTypes.PIPE );
create = true;
expr();
break;
}
}
getXPathHandler().endUnionExpr( create );
}
private Token match(int tokenType) throws XPathSyntaxException
{
LT(1);
Token token = (Token) tokens.get( 0 );
if ( token.getTokenType() == tokenType )
{
tokens.remove(0);
return token;
}
XPathSyntaxException ex = createSyntaxException( "Expected: " + TokenTypes.getTokenText( tokenType ) );
throw ex;
}
private int LA(int position)
{
return LT(position).getTokenType();
}
// XXX This method's a HotSpot; could we improve it?
private Token LT(int position)
{
if ( tokens.size() <= ( position - 1 ) )
{
for ( int i = 0 ; i < position ; ++i )
{
tokens.add( lexer.nextToken() );
}
}
return (Token) tokens.get( position - 1 );
}
private boolean isNodeTypeName(Token name)
{
String text = name.getTokenText();
if ( "node".equals( text )
||
"comment".equals( text )
||
"text".equals( text )
||
"processing-instruction".equals( text ) )
{
return true;
}
return false;
}
private XPathSyntaxException createSyntaxException(String message)
{
String xpath = this.lexer.getXPath();
int position = LT(1).getTokenBegin();
return new XPathSyntaxException( xpath,
position,
message );
}
private void throwInvalidAxis(String invalidAxis) throws SAXPathException
{
String xpath = this.lexer.getXPath();
int position = LT(1).getTokenBegin();
String message = "Expected valid axis name instead of [" + invalidAxis + "]";
throw new XPathSyntaxException( xpath,
position,
message );
}
}