All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.xpath.XPathParser Maven / Gradle / Ivy

There is a newer version: 4.0.66
Show newest version
/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source 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.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.xpath;

import com.caucho.server.util.CauchoSystem;
import com.caucho.util.CharBuffer;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import com.caucho.xml.XmlChar;
import com.caucho.xpath.expr.*;
import com.caucho.xpath.functions.BaseURI;
import com.caucho.xpath.functions.ResolveURI;
import com.caucho.xpath.functions.Trace;
import com.caucho.xpath.pattern.*;

import org.w3c.dom.Node;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Logger;


/**
 * Parses an XPath expression.
 */
class XPathParser {
  private static final Logger log
    = Logger.getLogger(XPathParser.class.getName());
  private static final L10N L = new L10N(XPathParser.class);

  private final static int ANCESTOR_AXIS = 0;
  private final static int ANCESTOR_OR_SELF_AXIS = ANCESTOR_AXIS + 1;
  private final static int ATTRIBUTE_AXIS = ANCESTOR_OR_SELF_AXIS + 1;
  private final static int CHILD_AXIS = ATTRIBUTE_AXIS + 1;
  private final static int DESCENDANT_AXIS = CHILD_AXIS + 1;
  private final static int DESCENDANT_OR_SELF_AXIS = DESCENDANT_AXIS + 1;
  private final static int FOLLOWING_AXIS = DESCENDANT_OR_SELF_AXIS + 1;
  private final static int FOLLOWING_SIBLING_AXIS = FOLLOWING_AXIS + 1;
  private final static int NAMESPACE_AXIS = FOLLOWING_SIBLING_AXIS + 1;
  private final static int PARENT_AXIS = NAMESPACE_AXIS + 1;
  private final static int PRECEDING_AXIS = PARENT_AXIS + 1;
  private final static int PRECEDING_SIBLING_AXIS = PRECEDING_AXIS + 1;
  private final static int SELF_AXIS = PRECEDING_SIBLING_AXIS + 1;

  private final static int TEXT = Expr.LAST_FUN + 1;
  private final static int COMMENT = TEXT + 1;
  private final static int ER = COMMENT + 1;
  private final static int PI = ER + 1;
  private final static int NODE = PI + 1;
  private final static int CURRENT = NODE + 1;
  private final static int NODE_TEXT = CURRENT + 1;
  private final static int CONTEXT = NODE_TEXT + 1;

  private static IntMap exprFunctions;
  private static IntMap axisMap;

  private static HashMap _exprFunctions;
  
  private CharBuffer tag = new CharBuffer();

  private String _string;
  private int index;
  private int peek;

  private NamespaceContext _namespace;

  XPathParser(String string, NamespaceContext namespace)
  {
    _string = string;
    _namespace = namespace;
  }

  /**
   * Parse a select pattern, i.e. a path rooted in a context.
   */
  AbstractPattern parseSelect()
    throws XPathParseException
  {
    AbstractPattern top = new FromContext();

    AbstractPattern pattern = parseUnion(parseTop(top), top);

    if (index < _string.length())
      throw error(L.l("unexpected character at `{0}'", badChar(read())));

    return pattern;
  }

  /**
   * Parse a match pattern, i.e. a path with no root.
   */
  AbstractPattern parseMatch()
    throws XPathParseException
  {
    AbstractPattern root = new FromAny();
    AbstractPattern pattern = parseUnion(parseTop(root), root);

    if (index < _string.length())
      throw error(L.l("unexpected character at `{0}'", badChar(read())));

    return pattern;
  }


  /**
   * Parse an expression.
   */
  Expr parseExpr()
    throws XPathParseException
  {
    Expr expr = parseExpr(null, null);

    if (index < _string.length())
      throw error(L.l("unexpected character at `{0}'", badChar(read())));

    return expr;
  }

  private AbstractPattern parseStep(AbstractPattern root)
    throws XPathParseException
  {
    return parseUnion(parseTop(root), root);
  }

  private AbstractPattern parseUnion(AbstractPattern left, AbstractPattern root)
    throws XPathParseException
  {
    int ch = skipWhitespace(read());

    while (ch >= 0) {
      if (ch == '|') {
        AbstractPattern tail = parseUnion(parseTop(root), root);
        left = new UnionPattern(left, tail);
      } else
        break;

      for (ch = read(); XmlChar.isWhitespace(ch); ch = read()) {
      }
    }

    unread();

    return left;
  }

  /**
   * Parses the top expression.
   *
   * 
   *  top ::= (expr)
   *      ::= term
   * 
*/ private AbstractPattern parseTop(AbstractPattern pattern) throws XPathParseException { int ch = skipWhitespace(read()); unread(); if (ch == '(') { Expr expr = parseTerm(pattern, pattern); // If the expression is really a pattern and the iterator is // ascending, then just unwrap it. if (expr instanceof NodeSetExpr) { AbstractPattern nodeSet = ((NodeSetExpr) expr).getPattern(); if (nodeSet.isAscending()) return nodeSet; } return new FromExpr(null, expr); } else return parseTerm(pattern, pattern).toNodeList(); } private AbstractPattern parseBasisTop(AbstractPattern pattern) throws XPathParseException { int ch; ch = skipWhitespace(read()); if (ch == '/') { ch = read(); if (ch == '/') { pattern = new FromRoot(); pattern = new FromDescendants(pattern, false); pattern = parseBasis(pattern); pattern = parseFilter(pattern); pattern = parsePath(pattern); return pattern; } pattern = new FromRoot(); ch = skipWhitespace(ch); if (ch == -1) return pattern; } unread(); if (pattern == null) pattern = new FromContext(); pattern = parseBasis(pattern); pattern = parseFilter(pattern); return parsePath(pattern); } /** * path ::= top ('/' filter)* */ private AbstractPattern parsePath(AbstractPattern pattern) throws XPathParseException { int ch = skipWhitespace(read()); while (ch == '/') { ch = read(); if (ch == '/') { pattern = new FromDescendants(pattern, false); pattern = parseBasis(pattern); pattern = parseFilter(pattern); } else { unread(); pattern = parseBasis(pattern); pattern = parseFilter(pattern); } ch = skipWhitespace(read()); } unread(); return pattern; } /** * filter ::= atom('[' expr ']')* */ private AbstractPattern parseFilter(AbstractPattern pattern) throws XPathParseException { int ch = skipWhitespace(read()); while (ch == '[') { AbstractPattern context = new FromContext(); Expr expr = parseExpr(context, pattern); pattern = new FilterPattern(pattern, expr); ch = skipWhitespace(read()); if (ch != ']') throw error(L.l("expected `{0}' at {1}", "]", badChar(ch))); ch = skipWhitespace(read()); } unread(); return pattern; } /** * basis ::= name::node-test * | node-test * | @node-test * | . * | .. * ; */ private AbstractPattern parseBasis(AbstractPattern pattern) throws XPathParseException { boolean fromChildren = true; int ch = skipWhitespace(read()); int nodeType = Node.ELEMENT_NODE; String namespace = null; tag.clear(); if (ch == '@') { if (pattern instanceof FromDescendants) pattern = NodeTypePattern.create(pattern, NodeTypePattern.NODE); pattern = new FromAttributes(pattern); nodeType = Node.ATTRIBUTE_NODE; fromChildren = false; ch = read(); } else if (ch == '.') { ch = read(); if (ch == '.') return NodeTypePattern.create(new FromParent(pattern), NodeTypePattern.NODE); else { unread(); if (pattern != null) return pattern; else return NodeTypePattern.create(new FromSelf(pattern), NodeTypePattern.ANY); } } else if (ch == '(') { // XXX: not strictly correct for counting Expr expr = parseExpr(null, null); if ((ch = read()) != ')') throw error(L.l("expected `{0}' at {1}", ")", badChar(ch))); return new FromExpr(pattern, expr); } if (ch == '*') { tag.append('*'); } else if (XmlChar.isNameStart(ch)) { for (; XmlChar.isNameChar(ch); ch = read()) tag.append((char) ch); if (ch == '*' && tag.endsWith(":")) tag.append('*'); else unread(); } else unread(); String name = tag.toString(); if (name.equals("")) throw error(L.l("expected name at {0}", badChar(ch))); return parseAxis(pattern, name, fromChildren, nodeType); } private AbstractPattern parseAxis(AbstractPattern pattern, String name, boolean fromChildren, int nodeType) throws XPathParseException { String axis = ""; int axisIndex = name.indexOf("::"); if (axisIndex >= 0 && nodeType != Node.ATTRIBUTE_NODE) { axis = name.substring(0, axisIndex); name = name.substring(axisIndex + 2); } if (pattern instanceof FromDescendants) return parseNodeTest(pattern, name, false, Node.ELEMENT_NODE); switch (axisMap.get(axis)) { case ANCESTOR_AXIS: return parseNodeTest(new FromAncestors(pattern, false), name, false, Node.ELEMENT_NODE); case ANCESTOR_OR_SELF_AXIS: return parseNodeTest(new FromAncestors(pattern, true), name, false, Node.ELEMENT_NODE); case ATTRIBUTE_AXIS: return parseNodeTest(new FromAttributes(pattern), name, false, Node.ATTRIBUTE_NODE); case CHILD_AXIS: return parseNodeTest(new FromChildren(pattern), name, false, Node.ELEMENT_NODE); case DESCENDANT_AXIS: return parseNodeTest(new FromDescendants(pattern, false), name, false, Node.ELEMENT_NODE); case DESCENDANT_OR_SELF_AXIS: return parseNodeTest(new FromDescendants(pattern, true), name, false, Node.ELEMENT_NODE); case FOLLOWING_AXIS: return parseNodeTest(new FromNext(pattern), name, false, Node.ELEMENT_NODE); case FOLLOWING_SIBLING_AXIS: return parseNodeTest(new FromNextSibling(pattern), name, false, Node.ELEMENT_NODE); case NAMESPACE_AXIS: return parseNodeTest(new FromNamespace(pattern), name, false, Node.ATTRIBUTE_NODE); case PARENT_AXIS: return parseNodeTest(new FromParent(pattern), name, false, Node.ELEMENT_NODE); case PRECEDING_AXIS: return parseNodeTest(new FromPrevious(pattern), name, false, Node.ELEMENT_NODE); case PRECEDING_SIBLING_AXIS: return parseNodeTest(new FromPreviousSibling(pattern), name, false, Node.ELEMENT_NODE); case SELF_AXIS: return parseNodeTest(new FromSelf(pattern), name, false, Node.ELEMENT_NODE); default: return parseNodeTest(pattern, name, fromChildren, nodeType); } } /** * node-test ::= qname * | *:name * | * * | name '(' args ')' * ; */ private AbstractPattern parseNodeTest(AbstractPattern pattern, String name, boolean fromChildren, int nodeType) throws XPathParseException { int ch = skipWhitespace(read()); AbstractPattern tagPattern; if (ch == '(') { Expr expr = parseFunction(pattern, pattern, name, fromChildren); return expr.toNodeList(); } else if (ch == '{') { tag.clear(); while ((ch = read()) >= 0 && ch != '}') tag.append((char) ch); String url = tag.toString(); tag.clear(); for (ch = read(); XmlChar.isNameChar(ch); ch = read()) tag.append((char) ch); pattern = new NSNamePattern(pattern, url, tag.toString(), nodeType); } else { if (fromChildren) pattern = new FromChildren(pattern); if (name.equals("*")) pattern = NodeTypePattern.create(pattern, nodeType); else if (name.endsWith(":*")) { pattern = new NamespacePattern(pattern, name.substring(0, name.length() - 2), nodeType); } else { int p = name.indexOf(':'); String ns = null; String local = name; if (p > 0) { String prefix = name.substring(0, p); ns = NamespaceContext.find(_namespace, prefix); local = name.substring(p + 1); } else if (nodeType != Node.ATTRIBUTE_NODE) ns = _namespace.find(_namespace, ""); else ns = null; // _namespace.find(_namespace, ""); if (ns == null) pattern = new NodePattern(pattern, name, nodeType); else pattern = new NSNamePattern(pattern, ns, local, nodeType); } } unread(); return pattern; } /** * expr ::= or-expr */ Expr parseExpr(AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { peek = -1; Expr left = parseTerm(parent, listParent); while (true) { int token = scanToken(); switch (token) { case Expr.OR: left = parseOrExpr(token, left, parseTerm(parent, listParent), parent, listParent); break; case Expr.AND: left = parseAndExpr(token, left, parseTerm(parent, listParent), parent, listParent); break; case Expr.EQ: case Expr.NEQ: case Expr.LT: case Expr.LE: case Expr.GT: case Expr.GE: left = parseCmpExpr(token, left, parseTerm(parent, listParent), parent, listParent); break; case Expr.ADD: case Expr.SUB: left = parseAddExpr(token, left, parseTerm(parent, listParent), parent, listParent); break; case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: left = parseMulExpr(token, left, parseTerm(parent, listParent), parent, listParent); break; default: return left; } } } /** * or-expr ::= or-expr 'OR' expr * | and-expr */ private Expr parseOrExpr(int code, Expr left, Expr right, AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { while (true) { int token = scanToken(); switch (token) { case Expr.OR: left = new BooleanExpr(code, left, right); code = token; right = parseTerm(parent, listParent); break; case Expr.AND: right = parseAndExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.EQ: case Expr.NEQ: case Expr.LT: case Expr.LE: case Expr.GT: case Expr.GE: right = parseCmpExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.ADD: case Expr.SUB: right = parseAddExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: right = parseMulExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; default: undoToken(token); return new BooleanExpr(code, left, right); } } } /** * and-expr ::= and-expr 'AND' expr * | cmp-expr */ private Expr parseAndExpr(int code, Expr left, Expr right, AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { while (true) { int token = scanToken(); switch (token) { case Expr.AND: left = new BooleanExpr(code, left, right); code = token; right = parseTerm(parent, listParent); break; case Expr.EQ: case Expr.NEQ: case Expr.LT: case Expr.LE: case Expr.GT: case Expr.GE: right = parseCmpExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.ADD: case Expr.SUB: right = parseAddExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: right = parseMulExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; default: undoToken(token); return new BooleanExpr(code, left, right); } } } /** * cmp-expr ::= cmp-expr '<' expr * | add-expr */ private Expr parseCmpExpr(int code, Expr left, Expr right, AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { while (true) { int token = scanToken(); switch (token) { case Expr.EQ: case Expr.NEQ: case Expr.LT: case Expr.LE: case Expr.GT: case Expr.GE: left = new BooleanExpr(code, left, right); code = token; right = parseTerm(parent, listParent); break; case Expr.ADD: case Expr.SUB: right = parseAddExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: right = parseMulExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; default: undoToken(token); return new BooleanExpr(code, left, right); } } } /** * add-expr ::= add-expr '+' expr * | mul-expr */ private Expr parseAddExpr(int code, Expr left, Expr right, AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { while (true) { int token = scanToken(); switch (token) { case Expr.ADD: case Expr.SUB: left = new NumericExpr(code, left, right); code = token; right = parseTerm(parent, listParent); break; case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: right = parseMulExpr(token, right, parseTerm(parent, listParent), parent, listParent); break; default: undoToken(token); return new NumericExpr(code, left, right); } } } /** * mul-expr ::= mul-expr '*' expr * | term */ private Expr parseMulExpr(int code, Expr left, Expr right, AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { while (true) { int token = scanToken(); switch (token) { case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD: left = new NumericExpr(code, left, right); code = token; right = parseTerm(parent, listParent); break; default: undoToken(token); return new NumericExpr(code, left, right); } } } /** * term ::= simple-term path? */ private Expr parseTerm(AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { int ch = skipWhitespace(read()); unread(); Expr expr = parseSimpleTerm(parent, listParent); int nextCh = skipWhitespace(read()); unread(); if (nextCh == '/' || nextCh == '[') { AbstractPattern pattern = expr.toNodeList(); if (ch == '(' && ! pattern.isStrictlyAscending()) pattern = new FromExpr(null, expr); return NodeSetExpr.create(parseUnion(parsePath(parseFilter(pattern)), pattern)); } else if (nextCh == '|') { AbstractPattern pattern = expr.toNodeList(); return NodeSetExpr.create(parseUnion(parsePath(parseFilter(pattern)), listParent)); } else return expr; } /** * simple-term ::= number * ::= '(' expr ')' * ::= '$' variable * ::= '"' string '"' * ::= node-set */ private Expr parseSimpleTerm(AbstractPattern parent, AbstractPattern listParent) throws XPathParseException { int ch = read(); ch = skipWhitespace(ch); switch (ch) { case '.': ch = read(); unread(); unread(); if (! ('0' <= ch && ch <= '9')) { return NodeSetExpr.create(parseUnion(parseBasisTop(parent), parent)); } ch = read(); // fall through to parse the number case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { long value = 0; double exp = 1; int digits = 0; for (; ch >= '0' && ch <= '9'; ch = read()) value = 10 * value + ch - '0'; if (ch == '.') { for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) { value = 10 * value + ch - '0'; exp *= 10; digits--; } } if (ch == 'e' || ch == 'E') { int sign = 1; int expValue = 0; ch = read(); if (ch == '-') { sign = -1; ch = read(); } else if (ch == '+') ch = read(); for (; ch >= '0' && ch <= '9'; ch = read()) expValue = 10 * expValue + ch - '0'; exp = Math.pow(10, digits + sign * expValue); unread(); return new NumericExpr((double) value * (double) exp); } unread(); return new NumericExpr((double) value / (double) exp); } case '-': return new NumericExpr(Expr.NEG, parseTerm(parent, listParent)); case '+': return parseTerm(parent, listParent); case '(': { Expr expr = parseExpr(parent, listParent); if ((ch = skipWhitespace(read())) != ')') throw error(L.l("expected `{0}' at {1}", ")", badChar(ch))); return expr; } case '/': case '@': case '*': unread(); return NodeSetExpr.create(parseUnion(parseBasisTop(parent), parent)); case '\'': case '"': { int end = ch; CharBuffer cb = new CharBuffer(); for (ch = read(); ch >= 0; ch = read()) { if (ch != end) cb.append((char) ch); else if ((ch = read()) == end) cb.append((char) ch); else { unread(); break; } } return new StringExpr(cb.toString()); } case '$': { String name = readName(read()); return new VarExpr(name); } default: if (! XmlChar.isNameStart(ch)) throw error(L.l("unknown character at {0}", badChar(ch))); String name = readName(ch); ch = skipWhitespace(read()); int axisIndex = name.indexOf("::"); // make sure axis are treated as node sets if (ch == '(' && axisIndex < 0) { return parseFunction(parent, listParent, name, true); } else if (ch == '(') { String axis = name.substring(0, axisIndex); if (axisMap.get(axis) <= 0) return parseFunction(parent, listParent, name, true); } unread(); if (parent == null) parent = new FromContext(); return parseNodeSetExpr(parent, name, Node.ELEMENT_NODE); } } /** * function ::= name '(' args ')' * * The XPath library functions are hard-coded for a little extra * execution efficiency. */ Expr parseFunction(AbstractPattern parent, AbstractPattern listParent, String name, boolean fromChildren) throws XPathParseException { int ch = skipWhitespace(read()); ArrayList args = new ArrayList(); for (; ch >= 0 && ch != ')'; ch = skipWhitespace(read())) { if (ch != ',') unread(); Expr expr = parseExpr(parent, listParent); if (expr == null) throw error(L.l("null expression")); args.add(expr); } int code = exprFunctions.get(name); switch (code) { case Expr.TRUE: case Expr.FALSE: case Expr.NOT: case Expr.BOOLEAN: case Expr.STARTS_WITH: case Expr.CONTAINS: case Expr.LANG: case Expr.FUNCTION_AVAILABLE: return new BooleanExpr(code, args); case Expr.NUMBER: case Expr.FLOOR: case Expr.CEILING: case Expr.ROUND: case Expr.STRING_LENGTH: return new NumericExpr(code, args); case Expr.POSITION: case Expr.LAST: return new NumericExpr(code, listParent); case Expr.COUNT: case Expr.SUM: if (args.size() == 0) args.add(NodeSetExpr.create(new FromContext())); return new NumericExpr(code, ((Expr) args.get(0)).toNodeList()); case Expr.CONCAT: case Expr.SUBSTRING_BEFORE: case Expr.SUBSTRING: case Expr.SUBSTRING_AFTER: case Expr.TRANSLATE: case Expr.SYSTEM_PROPERTY: return new StringExpr(code, args); case Expr.STRING: case Expr.NORMALIZE: return new StringExpr(code, args); case Expr.LOCAL_PART: case Expr.NAMESPACE: case Expr.QNAME: case Expr.GENERATE_ID: if (args.size() == 0) args.add(NodeSetExpr.create(new FromContext())); return new StringExpr(code, args); case Expr.ID: if (args.size() == 0) { args.add(NodeSetExpr.create(parent)); return new IdExpr(args); } else return new IdExpr(args); case Expr.IF: if (args.size() != 3) throw error(L.l("`if' needs three args.")); return new ObjectExpr(code, args); case Expr.BASE_URI: if (args.size() != 1) throw error(L.l("`base-uri' needs one args.")); return new StringExpr(code, args.get(0)); case TEXT: if (fromChildren) parent = new FromChildren(parent); AbstractPattern pattern = NodeTypePattern.create(parent, Node.TEXT_NODE); return NodeSetExpr.create(pattern); case COMMENT: if (fromChildren) parent = new FromChildren(parent); pattern = NodeTypePattern.create(parent, Node.COMMENT_NODE); return NodeSetExpr.create(pattern); case ER: if (fromChildren) parent = new FromChildren(parent); pattern = NodeTypePattern.create(parent, Node.ENTITY_REFERENCE_NODE); return NodeSetExpr.create(pattern); case PI: if (fromChildren) parent = new FromChildren(parent); if (args.size() == 1) { Expr expr = (Expr) args.get(0); String value = null; if (expr instanceof StringExpr) value = ((StringExpr) expr).getValue(); if (value == null) throw error(L.l("processing-instruction expects string literal")); pattern = new NodePattern(parent, value, Node.PROCESSING_INSTRUCTION_NODE); } else pattern = NodeTypePattern.create(parent, Node.PROCESSING_INSTRUCTION_NODE); return NodeSetExpr.create(pattern); case NODE: if (fromChildren) parent = new FromChildren(parent); pattern = NodeTypePattern.create(parent, NodeTypePattern.NODE); return NodeSetExpr.create(pattern); case CURRENT: return NodeSetExpr.create(new CurrentPattern()); case CONTEXT: return NodeSetExpr.create(new FromContext()); default: Expr function = constructorFunction(name, args); if (function != null) return function; int p = name.lastIndexOf(':'); String prefix; if (p > 0) prefix = name.substring(0, p); else prefix = ""; String context = NamespaceContext.find(_namespace, prefix); if (context == null) { } else if (context.startsWith("java:")) name = context + "." + name.substring(p + 1); else if (context.indexOf(':') < 0) name = "java:" + context + "." + name.substring(p + 1); if (name.startsWith("java:")) { p = name.lastIndexOf('.'); if (p < 0) throw error(L.l("`{0}' is an illegal extension function. Java extension functions must look like java:mypkg.MyClass.mymethod.", name)); String className = name.substring(5, p); String methodName = name.substring(p + 1); Class cl; try { cl = CauchoSystem.loadClass(className); } catch (ClassNotFoundException e) { throw error(L.l("`{0}' is an unknown Java class. Java extension functions must use public classes.", className)); } if (methodName.equals("new")) { Constructor []constructors = cl.getConstructors(); for (int i = 0; i < constructors.length; i++) { if (constructors[i].getParameterTypes().length == args.size()) return new NewJavaExpr(constructors[i], args); } throw error(L.l("No matching public constructor in `{0}'", className)); } Method method = null; Method []methods = cl.getMethods(); if (args.size() > 0) { for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == args.size() - 1 && ! Modifier.isStatic(methods[i].getModifiers())) { Expr objArg = (Expr) args.remove(0); return new ObjectJavaExpr(methods[i], objArg, args); } } } for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == args.size()) { method = methods[i]; break; } } if (method == null) throw error(L.l("`{0}' does not match a public method in `{1}'", methodName, className)); if (! Modifier.isStatic(method.getModifiers())) throw error(L.l("`{0}' is not a static method in `{1}'", methodName, className)); return new StaticJavaExpr(method, args); } else if (name.equals("")) throw error(L.l("expected node-test at `{0}'", "(")); return new FunExpr(name, parent, args); } } private Expr constructorFunction(String name, ArrayList args) throws XPathParseException { Constructor constructor = _exprFunctions.get(name); if (constructor == null) return null; Class []params = constructor.getParameterTypes(); if (params.length < args.size()) throw error(L.l("`{0}' needs {1} arguments", name, "" + params.length)); Object []values = new Object[params.length]; for (int i = 0; i < args.size(); i++) values[i] = args.get(i); try { return (Expr) constructor.newInstance(values); } catch (Throwable e) { throw new XPathParseException(e); } } private Expr parseNodeSetExpr(AbstractPattern parent, String name, int nodeType) throws XPathParseException { AbstractPattern top = parseAxis(parent, name, true, nodeType); top = parseFilter(top); return NodeSetExpr.create(parseUnion(parsePath(top), parent)); } /** * Scans the next token. * * @return token code, expressed as an Expr enumeration. */ private int scanToken() throws XPathParseException { if (peek >= 0) { int value = peek; peek = -1; return value; } int ch = skipWhitespace(read()); switch (ch) { case '+': return Expr.ADD; case '-': return Expr.SUB; case '*': return Expr.MUL; case '=': return Expr.EQ; case '!': ch = read(); if (ch == '=') return Expr.NEQ; else throw error(L.l("expected `{0}' at {1}", "=", badChar(ch))); case '<': ch = read(); if (ch == '=') return Expr.LE; else { unread(); return Expr.LT; } case '>': ch = read(); if (ch == '=') return Expr.GE; else { unread(); return Expr.GT; } default: if (XmlChar.isNameStart(ch)) { String name = readName(ch); if (name.equals("div")) return Expr.DIV; else if (name.equals("quo")) return Expr.QUO; else if (name.equals("mod")) return Expr.MOD; else if (name.equals("and")) return Expr.AND; else if (name.equals("or")) return Expr.OR; else throw error(L.l("expected binary operation at `{0}'", name)); } unread(); return -1; } } private String readName(int ch) { tag.clear(); for (; XmlChar.isNameChar(ch); ch = read()) tag.append((char) ch); if (ch == '*' && tag.endsWith(":")) tag.append((char) ch); else unread(); return tag.toString(); } private void undoToken(int token) { peek = token; } private int read() { if (index < _string.length()) { return _string.charAt(index++); } else { index++; return -1; } } private void unread() { index--; } private XPathParseException error(String message) { return new XPathParseException(message + " in " + _string); } private String badChar(int ch) { if (ch < 0) return L.l("end of file"); else if (ch == '\n') return L.l("end of line"); else return "`" + (char) ch + "'"; } private int skipWhitespace(int ch) throws XPathParseException { for (; ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; ch = read()) { } return ch; } private static void addFunction(String name, Class cl) { Constructor []constructors = cl.getConstructors(); _exprFunctions.put(name, constructors[0]); } static { exprFunctions = new IntMap(); exprFunctions.put("id", Expr.ID); exprFunctions.put("true", Expr.TRUE); exprFunctions.put("false", Expr.FALSE); exprFunctions.put("not", Expr.NOT); exprFunctions.put("boolean", Expr.BOOLEAN); exprFunctions.put("starts-with", Expr.STARTS_WITH); exprFunctions.put("contains", Expr.CONTAINS); exprFunctions.put("lang", Expr.LANG); exprFunctions.put("number", Expr.NUMBER); exprFunctions.put("sum", Expr.SUM); exprFunctions.put("floor", Expr.FLOOR); exprFunctions.put("ceiling", Expr.CEILING); exprFunctions.put("round", Expr.ROUND); exprFunctions.put("position", Expr.POSITION); exprFunctions.put("count", Expr.COUNT); exprFunctions.put("last", Expr.LAST); exprFunctions.put("string-length", Expr.STRING_LENGTH); exprFunctions.put("string", Expr.STRING); exprFunctions.put("concat", Expr.CONCAT); exprFunctions.put("substring", Expr.SUBSTRING); exprFunctions.put("substring-before", Expr.SUBSTRING_BEFORE); exprFunctions.put("substring-after", Expr.SUBSTRING_AFTER); exprFunctions.put("normalize-space", Expr.NORMALIZE); exprFunctions.put("translate", Expr.TRANSLATE); exprFunctions.put("local-name", Expr.LOCAL_PART); exprFunctions.put("local-part", Expr.LOCAL_PART); exprFunctions.put("namespace-uri", Expr.NAMESPACE); exprFunctions.put("name", Expr.QNAME); exprFunctions.put("generate-id", Expr.GENERATE_ID); exprFunctions.put("if", Expr.IF); exprFunctions.put("text", TEXT); exprFunctions.put("comment", COMMENT); exprFunctions.put("er", ER); exprFunctions.put("entity-reference", ER); exprFunctions.put("pi", PI); exprFunctions.put("processing-instruction", PI); exprFunctions.put("node", NODE); exprFunctions.put("current", CURRENT); exprFunctions.put("context", CONTEXT); axisMap = new IntMap(); axisMap.put("ancestor", ANCESTOR_AXIS); axisMap.put("ancestor-or-self", ANCESTOR_OR_SELF_AXIS); axisMap.put("attribute", ATTRIBUTE_AXIS); axisMap.put("child", CHILD_AXIS); axisMap.put("descendant", DESCENDANT_AXIS); axisMap.put("descendant-or-self", DESCENDANT_OR_SELF_AXIS); axisMap.put("following", FOLLOWING_AXIS); axisMap.put("following-sibling", FOLLOWING_SIBLING_AXIS); axisMap.put("namespace", NAMESPACE_AXIS); axisMap.put("parent", PARENT_AXIS); axisMap.put("preceding", PRECEDING_AXIS); axisMap.put("preceding-sibling", PRECEDING_SIBLING_AXIS); axisMap.put("self", SELF_AXIS); _exprFunctions = new HashMap(); addFunction("fn:base-uri", BaseURI.class); addFunction("fn:resolve-uri", ResolveURI.class); addFunction("fn:trace", Trace.class); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy