/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: Compiler.java,v 1.36 2004/02/17 04:32:49 minchau Exp $ */ package com.sun.org.apache.xpath.internal.compiler; import javax.xml.transform.ErrorListener; import javax.xml.transform.SourceLocator; import javax.xml.transform.TransformerException; import com.sun.org.apache.xalan.internal.res.XSLMessages; import com.sun.org.apache.xml.internal.dtm.Axis; import com.sun.org.apache.xml.internal.dtm.DTMFilter; import com.sun.org.apache.xml.internal.dtm.DTMIterator; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xml.internal.utils.QName; import com.sun.org.apache.xml.internal.utils.SAXSourceLocator; import com.sun.org.apache.xpath.internal.Expression; import com.sun.org.apache.xpath.internal.axes.UnionPathIterator; import com.sun.org.apache.xpath.internal.axes.WalkerFactory; import com.sun.org.apache.xpath.internal.functions.FuncExtFunction; import com.sun.org.apache.xpath.internal.functions.Function; import com.sun.org.apache.xpath.internal.functions.WrongNumberArgsException; import com.sun.org.apache.xpath.internal.objects.XNumber; import com.sun.org.apache.xpath.internal.objects.XString; import com.sun.org.apache.xpath.internal.operations.And; import com.sun.org.apache.xpath.internal.operations.Div; import com.sun.org.apache.xpath.internal.operations.Equals; import com.sun.org.apache.xpath.internal.operations.Gt; import com.sun.org.apache.xpath.internal.operations.Gte; import com.sun.org.apache.xpath.internal.operations.Lt; import com.sun.org.apache.xpath.internal.operations.Lte; import com.sun.org.apache.xpath.internal.operations.Minus; import com.sun.org.apache.xpath.internal.operations.Mod; import com.sun.org.apache.xpath.internal.operations.Mult; import com.sun.org.apache.xpath.internal.operations.Neg; import com.sun.org.apache.xpath.internal.operations.NotEquals; import com.sun.org.apache.xpath.internal.operations.Operation; import com.sun.org.apache.xpath.internal.operations.Or; import com.sun.org.apache.xpath.internal.operations.Plus; import com.sun.org.apache.xpath.internal.operations.UnaryOperation; import com.sun.org.apache.xpath.internal.operations.Variable; import com.sun.org.apache.xpath.internal.patterns.FunctionPattern; import com.sun.org.apache.xpath.internal.patterns.NodeTest; import com.sun.org.apache.xpath.internal.patterns.StepPattern; import com.sun.org.apache.xpath.internal.patterns.UnionPattern; import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; /** * An instance of this class compiles an XPath string expression into * a Expression object. This class compiles the string into a sequence * of operation codes (op map) and then builds from that into an Expression * tree. * @xsl.usage advanced */ public class Compiler extends OpMap { /** * Construct a Compiler object with a specific ErrorListener and * SourceLocator where the expression is located. * * @param errorHandler Error listener where messages will be sent, or null * if messages should be sent to System err. * @param locator The location object where the expression lives, which * may be null, but which, if not null, must be valid over * the long haul, in other words, it will not be cloned. */ public Compiler(ErrorListener errorHandler, SourceLocator locator) { m_errorHandler = errorHandler; m_locator = locator; } /** * Construct a Compiler instance that has a null error listener and a * null source locator. */ public Compiler() { m_errorHandler = null; m_locator = null; } /** * Execute the XPath object from a given opcode position. * @param xctxt The execution context. * @param context The current source tree context node. * @param opPos The current position in the xpath.m_opMap array. * @param callback Interface that implements the processLocatedNode method. * @param callbackInfo Object that will be passed to the processLocatedNode method. * @return The result of the XPath. * * @throws TransformerException if there is a syntax or other error. * @xsl.usage advanced */ public Expression compile(int opPos) throws TransformerException { int op = getOp(opPos); Expression expr = null; // System.out.println(getPatternString()+"op: "+op); switch (op) { case OpCodes.OP_XPATH : expr = compile(opPos + 2); break; case OpCodes.OP_OR : expr = or(opPos); break; case OpCodes.OP_AND : expr = and(opPos); break; case OpCodes.OP_NOTEQUALS : expr = notequals(opPos); break; case OpCodes.OP_EQUALS : expr = equals(opPos); break; case OpCodes.OP_LTE : expr = lte(opPos); break; case OpCodes.OP_LT : expr = lt(opPos); break; case OpCodes.OP_GTE : expr = gte(opPos); break; case OpCodes.OP_GT : expr = gt(opPos); break; case OpCodes.OP_PLUS : expr = plus(opPos); break; case OpCodes.OP_MINUS : expr = minus(opPos); break; case OpCodes.OP_MULT : expr = mult(opPos); break; case OpCodes.OP_DIV : expr = div(opPos); break; case OpCodes.OP_MOD : expr = mod(opPos); break; // case OpCodes.OP_QUO : // expr = quo(opPos); break; case OpCodes.OP_NEG : expr = neg(opPos); break; case OpCodes.OP_STRING : expr = string(opPos); break; case OpCodes.OP_BOOL : expr = bool(opPos); break; case OpCodes.OP_NUMBER : expr = number(opPos); break; case OpCodes.OP_UNION : expr = union(opPos); break; case OpCodes.OP_LITERAL : expr = literal(opPos); break; case OpCodes.OP_VARIABLE : expr = variable(opPos); break; case OpCodes.OP_GROUP : expr = group(opPos); break; case OpCodes.OP_NUMBERLIT : expr = numberlit(opPos); break; case OpCodes.OP_ARGUMENT : expr = arg(opPos); break; case OpCodes.OP_EXTFUNCTION : expr = compileExtension(opPos); break; case OpCodes.OP_FUNCTION : expr = compileFunction(opPos); break; case OpCodes.OP_LOCATIONPATH : expr = locationPath(opPos); break; case OpCodes.OP_PREDICATE : expr = null; break; // should never hit this here. case OpCodes.OP_MATCHPATTERN : expr = matchPattern(opPos + 2); break; case OpCodes.OP_LOCATIONPATHPATTERN : expr = locationPathPattern(opPos); break; case OpCodes.OP_QUO: error(XPATHErrorResources.ER_UNKNOWN_OPCODE, new Object[]{ "quo" }); //"ERROR! Unknown op code: "+m_opMap[opPos]); break; default : error(XPATHErrorResources.ER_UNKNOWN_OPCODE, new Object[]{ Integer.toString(getOp(opPos)) }); //"ERROR! Unknown op code: "+m_opMap[opPos]); } // if(null != expr) // expr.setSourceLocator(m_locator); return expr; } /** * Bottle-neck compilation of an operation with left and right operands. * * @param operation non-null reference to parent operation. * @param opPos The op map position of the parent operation. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Operation} instance. * * @throws TransformerException if there is a syntax or other error. */ private Expression compileOperation(Operation operation, int opPos) throws TransformerException { int leftPos = getFirstChildPos(opPos); int rightPos = getNextOpPos(leftPos); operation.setLeftRight(compile(leftPos), compile(rightPos)); return operation; } /** * Bottle-neck compilation of a unary operation. * * @param unary The parent unary operation. * @param opPos The position in the op map of the parent operation. * * @return The unary argument. * * @throws TransformerException if syntax or other error occurs. */ private Expression compileUnary(UnaryOperation unary, int opPos) throws TransformerException { int rightPos = getFirstChildPos(opPos); unary.setRight(compile(rightPos)); return unary; } /** * Compile an 'or' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Or} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression or(int opPos) throws TransformerException { return compileOperation(new Or(), opPos); } /** * Compile an 'and' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.And} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression and(int opPos) throws TransformerException { return compileOperation(new And(), opPos); } /** * Compile a '!=' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.NotEquals} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression notequals(int opPos) throws TransformerException { return compileOperation(new NotEquals(), opPos); } /** * Compile a '=' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Equals} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression equals(int opPos) throws TransformerException { return compileOperation(new Equals(), opPos); } /** * Compile a '<=' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Lte} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression lte(int opPos) throws TransformerException { return compileOperation(new Lte(), opPos); } /** * Compile a '<' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Lt} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression lt(int opPos) throws TransformerException { return compileOperation(new Lt(), opPos); } /** * Compile a '>=' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Gte} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression gte(int opPos) throws TransformerException { return compileOperation(new Gte(), opPos); } /** * Compile a '>' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Gt} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression gt(int opPos) throws TransformerException { return compileOperation(new Gt(), opPos); } /** * Compile a '+' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Plus} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression plus(int opPos) throws TransformerException { return compileOperation(new Plus(), opPos); } /** * Compile a '-' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Minus} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression minus(int opPos) throws TransformerException { return compileOperation(new Minus(), opPos); } /** * Compile a '*' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Mult} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression mult(int opPos) throws TransformerException { return compileOperation(new Mult(), opPos); } /** * Compile a 'div' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Div} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression div(int opPos) throws TransformerException { return compileOperation(new Div(), opPos); } /** * Compile a 'mod' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Mod} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression mod(int opPos) throws TransformerException { return compileOperation(new Mod(), opPos); } /* * Compile a 'quo' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Quo} instance. * * @throws TransformerException if a error occurs creating the Expression. */ // protected Expression quo(int opPos) throws TransformerException // { // return compileOperation(new Quo(), opPos); // } /** * Compile a unary '-' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Neg} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression neg(int opPos) throws TransformerException { return compileUnary(new Neg(), opPos); } /** * Compile a 'string(...)' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.String} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression string(int opPos) throws TransformerException { return compileUnary(new com.sun.org.apache.xpath.internal.operations.String(), opPos); } /** * Compile a 'boolean(...)' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Bool} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression bool(int opPos) throws TransformerException { return compileUnary(new com.sun.org.apache.xpath.internal.operations.Bool(), opPos); } /** * Compile a 'number(...)' operation. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Number} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression number(int opPos) throws TransformerException { return compileUnary(new com.sun.org.apache.xpath.internal.operations.Number(), opPos); } /** * Compile a literal string value. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.objects.XString} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression literal(int opPos) { opPos = getFirstChildPos(opPos); return (XString) getTokenQueue().elementAt(getOp(opPos)); } /** * Compile a literal number value. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.objects.XNumber} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression numberlit(int opPos) { opPos = getFirstChildPos(opPos); return (XNumber) getTokenQueue().elementAt(getOp(opPos)); } /** * Compile a variable reference. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.operations.Variable} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression variable(int opPos) throws TransformerException { Variable var = new Variable(); opPos = getFirstChildPos(opPos); int nsPos = getOp(opPos); java.lang.String namespace = (OpCodes.EMPTY == nsPos) ? null : (java.lang.String) getTokenQueue().elementAt(nsPos); java.lang.String localname = (java.lang.String) getTokenQueue().elementAt(getOp(opPos+1)); QName qname = new QName(namespace, localname); var.setQName(qname); return var; } /** * Compile an expression group. * * @param opPos The current position in the m_opMap array. * * @return reference to the contained expression. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression group(int opPos) throws TransformerException { // no-op return compile(opPos + 2); } /** * Compile a function argument. * * @param opPos The current position in the m_opMap array. * * @return reference to the argument expression. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression arg(int opPos) throws TransformerException { // no-op return compile(opPos + 2); } /** * Compile a location path union. The UnionPathIterator itself may create * {@link com.sun.org.apache.xpath.internal.axes.LocPathIterator} children. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.axes.LocPathIterator} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression union(int opPos) throws TransformerException { locPathDepth++; try { return UnionPathIterator.createUnionIterator(this, opPos); } finally { locPathDepth--; } } private int locPathDepth = -1; /** * Get the level of the location path or union being constructed. * @return 0 if it is a top-level path. */ public int getLocationPathDepth() { return locPathDepth; } /** * Compile a location path. The LocPathIterator itself may create * {@link com.sun.org.apache.xpath.internal.axes.AxesWalker} children. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.axes.LocPathIterator} instance. * * @throws TransformerException if a error occurs creating the Expression. */ public Expression locationPath(int opPos) throws TransformerException { locPathDepth++; try { DTMIterator iter = WalkerFactory.newDTMIterator(this, opPos, (locPathDepth == 0)); return (Expression)iter; // cast OK, I guess. } finally { locPathDepth--; } } /** * Compile a location step predicate expression. * * @param opPos The current position in the m_opMap array. * * @return the contained predicate expression. * * @throws TransformerException if a error occurs creating the Expression. */ public Expression predicate(int opPos) throws TransformerException { return compile(opPos + 2); } /** * Compile an entire match pattern expression. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.patterns.UnionPattern} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected Expression matchPattern(int opPos) throws TransformerException { locPathDepth++; try { // First, count... int nextOpPos = opPos; int i; for (i = 0; getOp(nextOpPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++) { nextOpPos = getNextOpPos(nextOpPos); } if (i == 1) return compile(opPos); UnionPattern up = new UnionPattern(); StepPattern[] patterns = new StepPattern[i]; for (i = 0; getOp(opPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++) { nextOpPos = getNextOpPos(opPos); patterns[i] = (StepPattern) compile(opPos); opPos = nextOpPos; } up.setPatterns(patterns); return up; } finally { locPathDepth--; } } /** * Compile a location match pattern unit expression. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.patterns.StepPattern} instance. * * @throws TransformerException if a error occurs creating the Expression. */ public Expression locationPathPattern(int opPos) throws TransformerException { opPos = getFirstChildPos(opPos); return stepPattern(opPos, 0, null); } /** * Get a {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what * to show for a given node test. * * @param opPos the op map position for the location step. * * @return {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what * to show for a given node test. */ public int getWhatToShow(int opPos) { int axesType = getOp(opPos); int testType = getOp(opPos + 3); // System.out.println("testType: "+testType); switch (testType) { case OpCodes.NODETYPE_COMMENT : return DTMFilter.SHOW_COMMENT; case OpCodes.NODETYPE_TEXT : // return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_COMMENT; return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION ; case OpCodes.NODETYPE_PI : return DTMFilter.SHOW_PROCESSING_INSTRUCTION; case OpCodes.NODETYPE_NODE : // return DTMFilter.SHOW_ALL; switch (axesType) { case OpCodes.FROM_NAMESPACE: return DTMFilter.SHOW_NAMESPACE; case OpCodes.FROM_ATTRIBUTES : case OpCodes.MATCH_ATTRIBUTE : return DTMFilter.SHOW_ATTRIBUTE; case OpCodes.FROM_SELF: case OpCodes.FROM_ANCESTORS_OR_SELF: case OpCodes.FROM_DESCENDANTS_OR_SELF: return DTMFilter.SHOW_ALL; default: if (getOp(0) == OpCodes.OP_MATCHPATTERN) return ~DTMFilter.SHOW_ATTRIBUTE & ~DTMFilter.SHOW_DOCUMENT & ~DTMFilter.SHOW_DOCUMENT_FRAGMENT; else return ~DTMFilter.SHOW_ATTRIBUTE; } case OpCodes.NODETYPE_ROOT : return DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT; case OpCodes.NODETYPE_FUNCTEST : return NodeTest.SHOW_BYFUNCTION; case OpCodes.NODENAME : switch (axesType) { case OpCodes.FROM_NAMESPACE : return DTMFilter.SHOW_NAMESPACE; case OpCodes.FROM_ATTRIBUTES : case OpCodes.MATCH_ATTRIBUTE : return DTMFilter.SHOW_ATTRIBUTE; // break; case OpCodes.MATCH_ANY_ANCESTOR : case OpCodes.MATCH_IMMEDIATE_ANCESTOR : return DTMFilter.SHOW_ELEMENT; // break; default : return DTMFilter.SHOW_ELEMENT; } default : // System.err.println("We should never reach here."); return DTMFilter.SHOW_ALL; } } private static final boolean DEBUG = false; /** * Compile a step pattern unit expression, used for both location paths * and match patterns. * * @param opPos The current position in the m_opMap array. * @param stepCount The number of steps to expect. * @param ancestorPattern The owning StepPattern, which may be null. * * @return reference to {@link com.sun.org.apache.xpath.internal.patterns.StepPattern} instance. * * @throws TransformerException if a error occurs creating the Expression. */ protected StepPattern stepPattern( int opPos, int stepCount, StepPattern ancestorPattern) throws TransformerException { int startOpPos = opPos; int stepType = getOp(opPos); if (OpCodes.ENDOP == stepType) { return null; } boolean addMagicSelf = true; int endStep = getNextOpPos(opPos); // int nextStepType = getOpMap()[endStep]; StepPattern pattern; // boolean isSimple = ((OpCodes.ENDOP == nextStepType) && (stepCount == 0)); int argLen; switch (stepType) { case OpCodes.OP_FUNCTION : if(DEBUG) System.out.println("MATCH_FUNCTION: "+m_currentPattern); addMagicSelf = false; argLen = getOp(opPos + OpMap.MAPINDEX_LENGTH); pattern = new FunctionPattern(compileFunction(opPos), Axis.PARENT, Axis.CHILD); break; case OpCodes.FROM_ROOT : if(DEBUG) System.out.println("FROM_ROOT, "+m_currentPattern); addMagicSelf = false; argLen = getArgLengthOfStep(opPos); opPos = getFirstChildPosOfStep(opPos); pattern = new StepPattern(DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT, Axis.PARENT, Axis.CHILD); break; case OpCodes.MATCH_ATTRIBUTE : if(DEBUG) System.out.println("MATCH_ATTRIBUTE: "+getStepLocalName(startOpPos)+", "+m_currentPattern); argLen = getArgLengthOfStep(opPos); opPos = getFirstChildPosOfStep(opPos); pattern = new StepPattern(DTMFilter.SHOW_ATTRIBUTE, getStepNS(startOpPos), getStepLocalName(startOpPos), Axis.PARENT, Axis.ATTRIBUTE); break; case OpCodes.MATCH_ANY_ANCESTOR : if(DEBUG) System.out.println("MATCH_ANY_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern); argLen = getArgLengthOfStep(opPos); opPos = getFirstChildPosOfStep(opPos); int what = getWhatToShow(startOpPos); // bit-o-hackery, but this code is due for the morgue anyway... if(0x00000500 == what) addMagicSelf = false; pattern = new StepPattern(getWhatToShow(startOpPos), getStepNS(startOpPos), getStepLocalName(startOpPos), Axis.ANCESTOR, Axis.CHILD); break; case OpCodes.MATCH_IMMEDIATE_ANCESTOR : if(DEBUG) System.out.println("MATCH_IMMEDIATE_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern); argLen = getArgLengthOfStep(opPos); opPos = getFirstChildPosOfStep(opPos); pattern = new StepPattern(getWhatToShow(startOpPos), getStepNS(startOpPos), getStepLocalName(startOpPos), Axis.PARENT, Axis.CHILD); break; default : error(XPATHErrorResources.ER_UNKNOWN_MATCH_OPERATION, null); //"unknown match operation!"); return null; } pattern.setPredicates(getCompiledPredicates(opPos + argLen)); if(null == ancestorPattern) { // This is the magic and invisible "." at the head of every // match pattern, and corresponds to the current node in the context // list, from where predicates are counted. // So, in order to calculate "foo[3]", it has to count from the // current node in the context list, so, from that current node, // the full pattern is really "self::node()/child::foo[3]". If you // translate this to a select pattern from the node being tested, // which is really how we're treating match patterns, it works out to // self::foo/parent::node[child::foo[3]]", or close enough. /* if(addMagicSelf && pattern.getPredicateCount() > 0) { StepPattern selfPattern = new StepPattern(DTMFilter.SHOW_ALL, Axis.PARENT, Axis.CHILD); // We need to keep the new nodetest from affecting the score... XNumber score = pattern.getStaticScore(); pattern.setRelativePathPattern(selfPattern); pattern.setStaticScore(score); selfPattern.setStaticScore(score); }*/ } else { // System.out.println("Setting "+ancestorPattern+" as relative to "+pattern); pattern.setRelativePathPattern(ancestorPattern); } StepPattern relativePathPattern = stepPattern(endStep, stepCount + 1, pattern); return (null != relativePathPattern) ? relativePathPattern : pattern; } /** * Compile a zero or more predicates for a given match pattern. * * @param opPos The position of the first predicate the m_opMap array. * * @return reference to array of {@link com.sun.org.apache.xpath.internal.Expression} instances. * * @throws TransformerException if a error occurs creating the Expression. */ public Expression[] getCompiledPredicates(int opPos) throws TransformerException { int count = countPredicates(opPos); if (count > 0) { Expression[] predicates = new Expression[count]; compilePredicates(opPos, predicates); return predicates; } return null; } /** * Count the number of predicates in the step. * * @param opPos The position of the first predicate the m_opMap array. * * @return The number of predicates for this step. * * @throws TransformerException if a error occurs creating the Expression. */ public int countPredicates(int opPos) throws TransformerException { int count = 0; while (OpCodes.OP_PREDICATE == getOp(opPos)) { count++; opPos = getNextOpPos(opPos); } return count; } /** * Compiles predicates in the step. * * @param opPos The position of the first predicate the m_opMap array. * @param predicates An empty pre-determined array of * {@link com.sun.org.apache.xpath.internal.Expression}s, that will be filled in. * * @throws TransformerException */ private void compilePredicates(int opPos, Expression[] predicates) throws TransformerException { for (int i = 0; OpCodes.OP_PREDICATE == getOp(opPos); i++) { predicates[i] = predicate(opPos); opPos = getNextOpPos(opPos); } } /** * Compile a built-in XPath function. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.functions.Function} instance. * * @throws TransformerException if a error occurs creating the Expression. */ Expression compileFunction(int opPos) throws TransformerException { int endFunc = opPos + getOp(opPos + 1) - 1; opPos = getFirstChildPos(opPos); int funcID = getOp(opPos); opPos++; if (-1 != funcID) { Function func = FunctionTable.getFunction(funcID); func.postCompileStep(this); try { int i = 0; for (int p = opPos; p < endFunc; p = getNextOpPos(p), i++) { // System.out.println("argPos: "+ p); // System.out.println("argCode: "+ m_opMap[p]); func.setArg(compile(p), i); } func.checkNumberArgs(i); } catch (WrongNumberArgsException wnae) { java.lang.String name = FunctionTable.getFunctionName(funcID); m_errorHandler.fatalError( new TransformerException( XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ONLY_ALLOWS, new Object[]{name, wnae.getMessage()}), m_locator)); //"name + " only allows " + wnae.getMessage() + " arguments", m_locator)); } return func; } else { error(XPATHErrorResources.ER_FUNCTION_TOKEN_NOT_FOUND, null); //"function token not found."); return null; } } // The current id for extension functions. private static long s_nextMethodId = 0; /** * Get the next available method id */ synchronized private long getNextMethodId() { if (s_nextMethodId == Long.MAX_VALUE) s_nextMethodId = 0; return s_nextMethodId++; } /** * Compile an extension function. * * @param opPos The current position in the m_opMap array. * * @return reference to {@link com.sun.org.apache.xpath.internal.functions.FuncExtFunction} instance. * * @throws TransformerException if a error occurs creating the Expression. */ private Expression compileExtension(int opPos) throws TransformerException { int endExtFunc = opPos + getOp(opPos + 1) - 1; opPos = getFirstChildPos(opPos); java.lang.String ns = (java.lang.String) getTokenQueue().elementAt(getOp(opPos)); opPos++; java.lang.String funcName = (java.lang.String) getTokenQueue().elementAt(getOp(opPos)); opPos++; // We create a method key to uniquely identify this function so that we // can cache the object needed to invoke it. This way, we only pay the // reflection overhead on the first call. Function extension = new FuncExtFunction(ns, funcName, String.valueOf(getNextMethodId())); try { int i = 0; while (opPos < endExtFunc) { int nextOpPos = getNextOpPos(opPos); extension.setArg(this.compile(opPos), i); opPos = nextOpPos; i++; } } catch (WrongNumberArgsException wnae) { ; // should never happen } return extension; } /** * Warn the user of an problem. * * @param msg An error msgkey that corresponds to one of the constants found * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is * a key for a format string. * @param args An array of arguments represented in the format string, which * may be null. * * @throws TransformerException if the current ErrorListoner determines to * throw an exception. */ public void warn(String msg, Object[] args) throws TransformerException { java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args); if (null != m_errorHandler) { m_errorHandler.warning(new TransformerException(fmsg, m_locator)); } else { System.out.println(fmsg +"; file "+m_locator.getSystemId() +"; line "+m_locator.getLineNumber() +"; column "+m_locator.getColumnNumber()); } } /** * Tell the user of an assertion error, and probably throw an * exception. * * @param b If false, a runtime exception will be thrown. * @param msg The assertion message, which should be informative. * * @throws RuntimeException if the b argument is false. */ public void assertion(boolean b, java.lang.String msg) { if (!b) { java.lang.String fMsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, new Object[]{ msg }); throw new RuntimeException(fMsg); } } /** * Tell the user of an error, and probably throw an * exception. * * @param msg An error msgkey that corresponds to one of the constants found * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is * a key for a format string. * @param args An array of arguments represented in the format string, which * may be null. * * @throws TransformerException if the current ErrorListoner determines to * throw an exception. */ public void error(String msg, Object[] args) throws TransformerException { java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args); if (null != m_errorHandler) { m_errorHandler.fatalError(new TransformerException(fmsg, m_locator)); } else { // System.out.println(te.getMessage() // +"; file "+te.getSystemId() // +"; line "+te.getLineNumber() // +"; column "+te.getColumnNumber()); throw new TransformerException(fmsg, (SAXSourceLocator)m_locator); } } /** * The current prefixResolver for the execution context. */ private PrefixResolver m_currentPrefixResolver = null; /** * Get the current namespace context for the xpath. * * @return The current prefix resolver, *may* be null, though hopefully not. */ public PrefixResolver getNamespaceContext() { return m_currentPrefixResolver; } /** * Set the current namespace context for the xpath. * * @param pr The resolver for prefixes in the XPath expression. */ public void setNamespaceContext(PrefixResolver pr) { m_currentPrefixResolver = pr; } /** The error listener where errors will be sent. If this is null, errors * and warnings will be sent to System.err. May be null. */ ErrorListener m_errorHandler; /** The source locator for the expression being compiled. May be null. */ SourceLocator m_locator; }