%{ /* * XPathParser.java * Copyright (C) 2004 The Free Software Foundation * * This file is part of GNU JAXP, a library. * * GNU JAXP 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 of the License, or * (at your option) any later version. * * GNU JAXP 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 * obliged 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 Chris Burdess */ 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); } ; %% }