| %{ | |
| /* XPathParser.y - An XPath 1.0 parser. | |
| Copyright (C) 2004 The Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.xml.xpath; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.xml.namespace.NamespaceContext; | |
| import javax.xml.namespace.QName; | |
| import javax.xml.xpath.XPathFunctionResolver; | |
| import javax.xml.xpath.XPathVariableResolver; | |
| import org.w3c.dom.Node; | |
| /** | |
| * An XPath 1.0 parser. | |
| * | |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> | |
| */ | |
| public class XPathParser | |
| { | |
| NamespaceContext namespaceContext; | |
| XPathVariableResolver variableResolver; | |
| XPathFunctionResolver functionResolver; | |
| QName getQName(String name) | |
| { | |
| QName qName = QName.valueOf(name); | |
| if (namespaceContext != null) | |
| { | |
| String prefix = qName.getPrefix(); | |
| String uri = qName.getNamespaceURI(); | |
| if (prefix != null && (uri == null || uri.length() == 0)) | |
| { | |
| uri = namespaceContext.getNamespaceURI(prefix); | |
| String localName = qName.getLocalPart(); | |
| qName = new QName(uri, localName, prefix); | |
| } | |
| } | |
| return qName; | |
| } | |
| Expr lookupFunction(String name, List args) | |
| { | |
| int arity = args.size(); | |
| if ("position".equals(name) && arity == 0) | |
| { | |
| return new PositionFunction(); | |
| } | |
| else if ("last".equals(name) && arity == 0) | |
| { | |
| return new LastFunction(); | |
| } | |
| else if ("string".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new StringFunction(args); | |
| } | |
| else if ("number".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new NumberFunction(args); | |
| } | |
| else if ("boolean".equals(name) && arity == 1) | |
| { | |
| return new BooleanFunction(args); | |
| } | |
| else if ("count".equals(name) && arity == 1) | |
| { | |
| return new CountFunction(args); | |
| } | |
| else if ("not".equals(name) && arity == 1) | |
| { | |
| return new NotFunction(args); | |
| } | |
| else if ("id".equals(name) && arity == 1) | |
| { | |
| return new IdFunction(args); | |
| } | |
| else if ("concat".equals(name) && arity > 1) | |
| { | |
| return new ConcatFunction(args); | |
| } | |
| else if ("true".equals(name) && arity == 0) | |
| { | |
| return new TrueFunction(); | |
| } | |
| else if ("false".equals(name) && arity == 0) | |
| { | |
| return new FalseFunction(); | |
| } | |
| else if ("name".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new NameFunction(args); | |
| } | |
| else if ("local-name".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new LocalNameFunction(args); | |
| } | |
| else if ("namespace-uri".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new NamespaceUriFunction(args); | |
| } | |
| else if ("starts-with".equals(name) && arity == 2) | |
| { | |
| return new StartsWithFunction(args); | |
| } | |
| else if ("contains".equals(name) && arity == 2) | |
| { | |
| return new ContainsFunction(args); | |
| } | |
| else if ("string-length".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new StringLengthFunction(args); | |
| } | |
| else if ("translate".equals(name) && arity == 3) | |
| { | |
| return new TranslateFunction(args); | |
| } | |
| else if ("normalize-space".equals(name) && (arity == 1 || arity == 0)) | |
| { | |
| return new NormalizeSpaceFunction(args); | |
| } | |
| else if ("substring".equals(name) && (arity == 2 || arity == 3)) | |
| { | |
| return new SubstringFunction(args); | |
| } | |
| else if ("substring-before".equals(name) && arity == 2) | |
| { | |
| return new SubstringBeforeFunction(args); | |
| } | |
| else if ("substring-after".equals(name) && arity == 2) | |
| { | |
| return new SubstringAfterFunction(args); | |
| } | |
| else if ("lang".equals(name) && arity == 1) | |
| { | |
| return new LangFunction(args); | |
| } | |
| else if ("sum".equals(name) && arity == 1) | |
| { | |
| return new SumFunction(args); | |
| } | |
| else if ("floor".equals(name) && arity == 1) | |
| { | |
| return new FloorFunction(args); | |
| } | |
| else if ("ceiling".equals(name) && arity == 1) | |
| { | |
| return new CeilingFunction(args); | |
| } | |
| else if ("round".equals(name) && arity == 1) | |
| { | |
| return new RoundFunction(args); | |
| } | |
| else if (functionResolver != null) | |
| { | |
| QName qName = QName.valueOf(name); | |
| Object function = functionResolver.resolveFunction(qName, arity); | |
| if (function != null && | |
| function instanceof Function && | |
| function instanceof Expr) | |
| { | |
| Function f = (Function) function; | |
| f.setArguments(args); | |
| return (Expr) function; | |
| } | |
| } | |
| return new FunctionCall(functionResolver, name, args); | |
| } | |
| %} | |
| %token LITERAL | |
| %token DIGITS | |
| %token NAME | |
| %token LP // '(' | |
| %token RP // ')' | |
| %token LB // '[' | |
| %token RB // ']' | |
| %token COMMA // ',' | |
| %token PIPE // '|' | |
| %token SLASH // '/' | |
| %token DOUBLE_SLASH // '//' | |
| %token EQ // '=' | |
| %token NE // '!=' | |
| %token GT // '>' | |
| %token LT // '<' | |
| %token GTE // '>=' | |
| %token LTE // '<=' | |
| %token PLUS // '+' | |
| %token MINUS // '-' | |
| %token AT // '@' | |
| %token STAR // '*' | |
| %token DOLLAR // '$' | |
| %token COLON // ':' | |
| %token DOUBLE_COLON // '::' | |
| %token DOT // '.' | |
| %token DOUBLE_DOT // '..' | |
| %token ANCESTOR | |
| %token ANCESTOR_OR_SELF | |
| %token ATTRIBUTE | |
| %token CHILD | |
| %token DESCENDANT | |
| %token DESCENDANT_OR_SELF | |
| %token FOLLOWING | |
| %token FOLLOWING_SIBLING | |
| %token NAMESPACE | |
| %token PARENT | |
| %token PRECEDING | |
| %token PRECEDING_SIBLING | |
| %token SELF | |
| %token DIV | |
| %token MOD | |
| %token OR | |
| %token AND | |
| %token COMMENT | |
| %token PROCESSING_INSTRUCTION | |
| %token TEXT | |
| %token NODE | |
| %right UNARY | |
| %start expr | |
| %% | |
| expr: | |
| or_expr | |
| ; | |
| location_path: | |
| relative_location_path | |
| | absolute_location_path | |
| ; | |
| absolute_location_path: | |
| SLASH | |
| { | |
| $$ = new Root(); | |
| } | |
| | SLASH relative_location_path | |
| { | |
| Steps steps; | |
| if ($2 instanceof Steps) | |
| { | |
| steps = (Steps) $2; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($2); | |
| } | |
| steps.path.addFirst(new Root()); | |
| $$ = steps; | |
| //$$ = new Step(new Root(), (Path) $2); | |
| } | |
| | DOUBLE_SLASH relative_location_path | |
| { | |
| Test nt = new NodeTypeTest((short) 0); | |
| Selector s = new Selector(Selector.DESCENDANT_OR_SELF, | |
| Collections.singletonList (nt)); | |
| Steps steps; | |
| if ($2 instanceof Steps) | |
| { | |
| steps = (Steps) $2; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($2); | |
| } | |
| steps.path.addFirst(s); | |
| steps.path.addFirst(new Root()); | |
| $$ = steps; | |
| //Step step = new Step(s, (Path) $2); | |
| //$$ = new Step(new Root(), step); | |
| } | |
| ; | |
| relative_location_path: | |
| step | |
| | relative_location_path SLASH step | |
| { | |
| Steps steps; | |
| if ($1 instanceof Steps) | |
| { | |
| steps = (Steps) $1; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($1); | |
| } | |
| steps.path.addLast($3); | |
| $$ = steps; | |
| //$$ = new Step((Expr) $1, (Path) $3); | |
| } | |
| | relative_location_path DOUBLE_SLASH step | |
| { | |
| Test nt = new NodeTypeTest((short) 0); | |
| Selector s = new Selector(Selector.DESCENDANT_OR_SELF, | |
| Collections.singletonList (nt)); | |
| Steps steps; | |
| if ($1 instanceof Steps) | |
| { | |
| steps = (Steps) $1; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($1); | |
| } | |
| steps.path.addLast(s); | |
| steps.path.addLast($3); | |
| $$ = steps; | |
| //Step step = new Step(s, (Path) $3); | |
| //$$ = new Step((Expr) $1, step); | |
| } | |
| ; | |
| step: | |
| step_node_test | |
| { | |
| $$ = new Selector (Selector.CHILD, (List) $1); | |
| } | |
| | AT step_node_test | |
| { | |
| $$ = new Selector (Selector.ATTRIBUTE, (List) $2); | |
| } | |
| | axis_name DOUBLE_COLON step_node_test | |
| { | |
| $$ = new Selector (((Integer) $1).intValue (), (List) $3); | |
| } | |
| | DOT | |
| { | |
| $$ = new Selector (Selector.SELF, Collections.EMPTY_LIST); | |
| } | |
| | DOUBLE_DOT | |
| { | |
| $$ = new Selector (Selector.PARENT, Collections.EMPTY_LIST); | |
| } | |
| ; | |
| step_node_test: | |
| node_test | |
| { | |
| List list = new ArrayList(); | |
| list.add($1); | |
| $$ = list; | |
| } | |
| | step_node_test predicate | |
| { | |
| List list = (List)$1; | |
| list.add($2); | |
| $$ = list; | |
| } | |
| ; | |
| /*predicate_list: | |
| predicate | |
| { | |
| List list = new ArrayList (); | |
| list.add ($1); | |
| $$ = list; | |
| } | |
| | predicate predicate_list | |
| { | |
| List list = (List) $3; | |
| list.add (0, $1); | |
| $$ = list; | |
| } | |
| ;*/ | |
| axis_name: | |
| ANCESTOR | |
| { | |
| $$ = new Integer(Selector.ANCESTOR); | |
| } | |
| | ANCESTOR_OR_SELF | |
| { | |
| $$ = new Integer(Selector.ANCESTOR_OR_SELF); | |
| } | |
| | ATTRIBUTE | |
| { | |
| $$ = new Integer(Selector.ATTRIBUTE); | |
| } | |
| | CHILD | |
| { | |
| $$ = new Integer(Selector.CHILD); | |
| } | |
| | DESCENDANT | |
| { | |
| $$ = new Integer(Selector.DESCENDANT); | |
| } | |
| | DESCENDANT_OR_SELF | |
| { | |
| $$ = new Integer(Selector.DESCENDANT_OR_SELF); | |
| } | |
| | FOLLOWING | |
| { | |
| $$ = new Integer(Selector.FOLLOWING); | |
| } | |
| | FOLLOWING_SIBLING | |
| { | |
| $$ = new Integer(Selector.FOLLOWING_SIBLING); | |
| } | |
| | NAMESPACE | |
| { | |
| $$ = new Integer(Selector.NAMESPACE); | |
| } | |
| | PARENT | |
| { | |
| $$ = new Integer(Selector.PARENT); | |
| } | |
| | PRECEDING | |
| { | |
| $$ = new Integer(Selector.PRECEDING); | |
| } | |
| | PRECEDING_SIBLING | |
| { | |
| $$ = new Integer(Selector.PRECEDING_SIBLING); | |
| } | |
| | SELF | |
| { | |
| $$ = new Integer(Selector.SELF); | |
| } | |
| ; | |
| node_test: | |
| name_test | |
| /*| PROCESSING_INSTRUCTION LP LITERAL RP*/ | |
| | PROCESSING_INSTRUCTION LITERAL RP | |
| { | |
| $$ = new NodeTypeTest(Node.PROCESSING_INSTRUCTION_NODE, (String) $2); | |
| } | |
| /*| node_type LP RP*/ | |
| | node_type RP | |
| { | |
| $$ = new NodeTypeTest(((Short) $1).shortValue()); | |
| } | |
| ; | |
| predicate: | |
| LB expr RB | |
| { | |
| $$ = new Predicate((Expr) $2); | |
| } | |
| ; | |
| primary_expr: | |
| variable_reference | |
| | LP expr RP | |
| { | |
| $$ = new ParenthesizedExpr((Expr) $2); | |
| } | |
| | LITERAL | |
| { | |
| $$ = new Constant($1); | |
| } | |
| | number | |
| { | |
| $$ = new Constant($1); | |
| } | |
| | function_call | |
| ; | |
| function_call: | |
| function_name LP RP | |
| { | |
| $$ = lookupFunction((String) $1, Collections.EMPTY_LIST); | |
| } | |
| | function_name LP argument_list RP | |
| { | |
| $$ = lookupFunction((String) $1, (List) $3); | |
| } | |
| ; | |
| argument_list: | |
| expr | |
| { | |
| List list = new ArrayList(); | |
| list.add($1); | |
| $$ = list; | |
| } | |
| | expr COMMA argument_list | |
| { | |
| List list = (List) $3; | |
| list.add(0, $1); | |
| $$ = list; | |
| } | |
| ; | |
| union_expr: | |
| path_expr | |
| | union_expr PIPE path_expr | |
| { | |
| $$ = new UnionExpr((Expr) $1, (Expr) $3); | |
| } | |
| ; | |
| path_expr: | |
| location_path | |
| | filter_expr | |
| | filter_expr SLASH relative_location_path | |
| { | |
| Steps steps; | |
| if ($3 instanceof Steps) | |
| { | |
| steps = (Steps) $3; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($3); | |
| } | |
| steps.path.addFirst($1); | |
| $$ = steps; | |
| //$$ = new Step ((Expr) $1, (Path) $3); | |
| } | |
| | filter_expr DOUBLE_SLASH relative_location_path | |
| { | |
| Test nt = new NodeTypeTest((short) 0); | |
| Selector s = new Selector(Selector.DESCENDANT_OR_SELF, | |
| Collections.singletonList(nt)); | |
| Steps steps; | |
| if ($3 instanceof Steps) | |
| { | |
| steps = (Steps) $3; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($3); | |
| } | |
| steps.path.addFirst(s); | |
| steps.path.addFirst($1); | |
| $$ = steps; | |
| //Step step = new Step (s, (Path) $3); | |
| //$$ = new Step ((Expr) $1, step); | |
| } | |
| ; | |
| filter_expr: | |
| primary_expr | |
| | filter_expr predicate | |
| { | |
| Predicate filter = (Predicate) $2; | |
| Selector s = new Selector(Selector.SELF, | |
| Collections.singletonList(filter)); | |
| Steps steps; | |
| if ($1 instanceof Steps) | |
| { | |
| steps = (Steps) $1; | |
| } | |
| else | |
| { | |
| steps = new Steps(); | |
| steps.path.addFirst($1); | |
| } | |
| steps.path.addLast(s); | |
| $$ = steps; | |
| //$$ = new Step ((Expr) $1, s); | |
| } | |
| ; | |
| or_expr: | |
| and_expr | |
| | or_expr OR and_expr | |
| { | |
| $$ = new OrExpr((Expr) $1, (Expr) $3); | |
| } | |
| ; | |
| and_expr: | |
| equality_expr | |
| | and_expr AND equality_expr | |
| { | |
| $$ = new AndExpr((Expr) $1, (Expr) $3); | |
| } | |
| ; | |
| equality_expr: | |
| relational_expr | |
| | equality_expr EQ relational_expr | |
| { | |
| $$ = new EqualityExpr((Expr) $1, (Expr) $3, false); | |
| } | |
| | equality_expr NE relational_expr | |
| { | |
| $$ = new EqualityExpr((Expr) $1, (Expr) $3, true); | |
| } | |
| ; | |
| relational_expr: | |
| additive_expr | |
| | relational_expr LT additive_expr | |
| { | |
| $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, false); | |
| } | |
| | relational_expr GT additive_expr | |
| { | |
| $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, false); | |
| } | |
| | relational_expr LTE additive_expr | |
| { | |
| $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, true); | |
| } | |
| | relational_expr GTE additive_expr | |
| { | |
| $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, true); | |
| } | |
| ; | |
| additive_expr: | |
| multiplicative_expr | |
| | additive_expr PLUS multiplicative_expr | |
| { | |
| $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.ADD); | |
| } | |
| | additive_expr MINUS multiplicative_expr | |
| { | |
| $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.SUBTRACT); | |
| } | |
| ; | |
| multiplicative_expr: | |
| unary_expr | |
| | multiplicative_expr STAR unary_expr | |
| { | |
| $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MULTIPLY); | |
| } | |
| | multiplicative_expr DIV unary_expr | |
| { | |
| $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.DIVIDE); | |
| } | |
| | multiplicative_expr MOD unary_expr | |
| { | |
| $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MODULO); | |
| } | |
| ; | |
| unary_expr: | |
| union_expr | |
| | MINUS unary_expr %prec UNARY | |
| { | |
| $$ = new NegativeExpr((Expr) $2); | |
| } | |
| ; | |
| number: | |
| DIGITS | |
| { | |
| $$ = new Double((String) $1 + ".0"); | |
| } | |
| | DIGITS DOT | |
| { | |
| $$ = new Double((String) $1 + ".0"); | |
| } | |
| | DIGITS DOT DIGITS | |
| { | |
| $$ = new Double((String) $1 + "." + (String) $3); | |
| } | |
| | DOT DIGITS | |
| { | |
| $$ = new Double("0." + (String) $2); | |
| } | |
| ; | |
| function_name: | |
| qname | |
| /* | node_type | |
| { | |
| switch (((Short) $1).shortValue ()) | |
| { | |
| case Node.COMMENT_NODE: | |
| $$ = "comment"; | |
| break; | |
| case Node.TEXT_NODE: | |
| $$ = "text"; | |
| break; | |
| case Node.PROCESSING_INSTRUCTION_NODE: | |
| $$ = "processing-instruction"; | |
| break; | |
| default: | |
| $$ = "node"; | |
| break; | |
| } | |
| }*/ | |
| ; | |
| variable_reference: | |
| DOLLAR qname | |
| { | |
| String name = (String) $2; | |
| $$ = new VariableReference(variableResolver, getQName(name)); | |
| } | |
| ; | |
| name_test: | |
| STAR | |
| { | |
| $$ = new NameTest(null, true, true); | |
| } | |
| | NAME COLON STAR | |
| { | |
| QName qName = getQName((String) $1); | |
| $$ = new NameTest(qName, true, false); | |
| } | |
| | qname | |
| { | |
| QName qName = getQName((String) $1); | |
| $$ = new NameTest(qName, false, false); | |
| } | |
| ; | |
| qname: | |
| NAME | |
| | NAME COLON NAME | |
| { | |
| $$ = (String) $1 + ':' + (String) $3; | |
| } | |
| ; | |
| node_type: | |
| COMMENT | |
| { | |
| $$ = new Short(Node.COMMENT_NODE); | |
| } | |
| | TEXT | |
| { | |
| $$ = new Short(Node.TEXT_NODE); | |
| } | |
| | PROCESSING_INSTRUCTION | |
| { | |
| $$ = new Short(Node.PROCESSING_INSTRUCTION_NODE); | |
| } | |
| | NODE | |
| { | |
| $$ = new Short((short) 0); | |
| } | |
| ; | |
| %% | |
| } |