blob: 20b30c6b98921a977fdd5eeb00bb4574354366b3 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xpath;
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.DocumentFragment;
import java.util.Vector;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.IOException;
import org.apache.xalan.utils.PrefixResolver;
import org.apache.xalan.utils.QName;
import org.apache.xpath.res.XPATHErrorResources;
import org.apache.xpath.functions.Function;
// import org.apache.xpath.functions.FuncLoader;
import org.apache.xpath.compiler.Compiler;
import org.apache.xpath.compiler.XPathParser;
import org.apache.xpath.compiler.OpMap; // temp
import org.apache.xpath.compiler.OpCodes; // temp
import org.apache.xpath.compiler.PsuedoNames; // temp
import org.apache.xpath.compiler.FunctionTable; // temp
import org.apache.xpath.objects.XObject;
import org.apache.xalan.res.XSLMessages;
import org.apache.xpath.objects.*;
import javax.xml.transform.TransformerException;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.ErrorListener;
import org.apache.xalan.utils.SAXSourceLocator;
import org.apache.xpath.patterns.NodeTest;
/**
* <meta name="usage" content="general"/>
* The XPath class represents the semantic parse tree of the XPath pattern.
* It is the representation of the grammar which filters out
* the choice for replacement order of the production rules.
* In order to conserve memory and reduce object creation, the
* tree is represented as an array of integers:
* [op code][length][...]
* where strings are represented within the array as
* indexes into the token tree.
*/
public class XPath implements Serializable
{
/** NEEDSDOC Field m_mainExp */
private Expression m_mainExp;
/**
* NEEDSDOC Method getExpression
*
*
* NEEDSDOC (getExpression) @return
*/
public Expression getExpression()
{
return m_mainExp;
}
/**
* NEEDSDOC Method setExpression
*
*
* NEEDSDOC @param exp
*/
public void setExpression(Expression exp)
{
m_mainExp = exp;
}
/** NEEDSDOC Field m_locator */
private SourceLocator m_locator;
/**
* NEEDSDOC Method getLocator
*
*
* NEEDSDOC (getLocator) @return
*/
public SourceLocator getLocator()
{
return m_locator;
}
/**
* NEEDSDOC Method setLocator
*
*
* NEEDSDOC @param l
*/
public void setLocator(SourceLocator l)
{
// Note potential hazards -- l may not be serializable, or may be changed
// after being assigned here.
m_locator = l;
}
/** NEEDSDOC Field m_patternString */
String m_patternString;
/**
* NEEDSDOC Method getPatternString
*
*
* NEEDSDOC (getPatternString) @return
*/
public String getPatternString()
{
return m_patternString;
}
/** NEEDSDOC Field SELECT */
public static final int SELECT = 0;
/** NEEDSDOC Field MATCH */
public static final int MATCH = 1;
/**
* Construct an XPath object. The object must be initialized by the
* XPathParser.initXPath method.
*
* NEEDSDOC @param exprString
* NEEDSDOC @param locator
* NEEDSDOC @param prefixResolver
* NEEDSDOC @param type
*
* @throws javax.xml.transform.TransformerException
*/
public XPath(
String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
throws javax.xml.transform.TransformerException
{
m_locator = locator;
m_patternString = exprString;
XPathParser parser = new XPathParser();
Compiler compiler = new Compiler(null, locator);
if (SELECT == type)
parser.initXPath(compiler, exprString, prefixResolver);
else if (MATCH == type)
parser.initMatchPattern(compiler, exprString, prefixResolver);
else
throw new RuntimeException("Can not deal with XPath type: " + type);
// System.out.println("----------------");
Expression expr = compiler.compile(0);
// System.out.println("expr: "+expr);
this.setExpression(expr);
if (MATCH == type)
calcTargetStrings(compiler);
}
/**
* <meta name="usage" content="experimental"/>
* Given an expression and a context, evaluate the XPath
* and call the callback as nodes are found. Only some simple
* types of expresions right now can call back, so if this
* method returns null, then the callbacks have been called, otherwise
* a valid XObject will be returned.
* @param xctxt The execution context.
* @param contextNode The node that "." expresses.
* @param namespaceContext The context in which namespaces in the
* XPath are supposed to be expanded.
* @exception TransformerException thrown if the active ProblemListener decides
* the error condition is severe enough to halt processing.
* @param callback Interface that implements the processLocatedNode method.
* @param callbackInfo Object that will be passed to the processLocatedNode method.
* @param stopAtFirst True if the search should stop once the first node in document
* order is found.
* @return The result of the XPath or null if callbacks are used.
* @exception TransformerException thrown if
* the error condition is severe enough to halt processing.
*
* @throws javax.xml.transform.TransformerException
*/
public XObject execute(
XPathContext xctxt, Node contextNode, PrefixResolver namespaceContext)
throws javax.xml.transform.TransformerException
{
PrefixResolver savedPrefixResolver = xctxt.getNamespaceContext();
xctxt.m_currentPrefixResolver = namespaceContext;
xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
XObject xobj = null;
try
{
xobj = m_mainExp.execute(xctxt);
}
catch (Exception e)
{
if (e instanceof javax.xml.transform.TransformerException)
{
TransformerException te = (TransformerException)e;
throw new TransformerException(te.getMessage(),
(SAXSourceLocator)te.getLocator(), e);
}
else
{
while (e instanceof org.apache.xalan.utils.WrappedRuntimeException)
{
e = ((org.apache.xalan.utils.WrappedRuntimeException) e).getException();
}
String msg = e.getMessage();
msg = (msg == null || msg.length()== 0)? "Error in XPath" : msg;
throw new TransformerException(msg,
(SAXSourceLocator)m_locator, e);
}
}
finally
{
xctxt.m_currentPrefixResolver = savedPrefixResolver;
xctxt.popCurrentNodeAndExpression();
}
return xobj;
}
/** NEEDSDOC Field DEBUG_MATCHES */
private static final boolean DEBUG_MATCHES = false;
/**
* Get the match score of the given node.
*
* NEEDSDOC @param xctxt
* @param context The current source tree context node.
* @returns score, one of MATCH_SCORE_NODETEST,
* MATCH_SCORE_NONE, MATCH_SCORE_OTHER, MATCH_SCORE_QNAME.
*
* NEEDSDOC ($objectName$) @return
*
* @throws javax.xml.transform.TransformerException
*/
public double getMatchScore(XPathContext xctxt, Node context)
throws javax.xml.transform.TransformerException
{
xctxt.pushCurrentNode(context);
xctxt.pushCurrentExpressionNode(context);
try
{
XObject score = m_mainExp.execute(xctxt);
if (DEBUG_MATCHES)
System.out.println("score: " + score.num() + " for "
+ context.getNodeName() + " for xpath "
+ this.getPatternString());
return score.num();
}
finally
{
xctxt.popCurrentNode();
xctxt.popCurrentExpressionNode();
}
// return XPath.MATCH_SCORE_NONE;
}
/**
* Install a built-in function.
* @param name The unqualified name of the function.
* @param funcIndex The index of the function in the table.
* @param func A Implementation of an XPath Function object.
* @return the position of the function in the internal index.
*/
public void installFunction(String name, int funcIndex, Function func)
{
FunctionTable.installFunction(func, funcIndex);
}
/** NEEDSDOC Field m_targetStrings */
private Vector m_targetStrings;
/**
* NEEDSDOC Method calcTargetStrings
*
*
* NEEDSDOC @param compiler
*/
private void calcTargetStrings(Compiler compiler)
{
Vector targetStrings = new Vector();
int opPos = 2;
while (compiler.getOp(opPos) == OpCodes.OP_LOCATIONPATHPATTERN)
{
int nextOpPos = compiler.getNextOpPos(opPos);
opPos = compiler.getFirstChildPos(opPos);
while (compiler.getOp(opPos) != OpCodes.ENDOP)
{
int nextStepPos = compiler.getNextOpPos(opPos);
int nextOp = compiler.getOp(nextStepPos);
if ((nextOp == OpCodes.OP_PREDICATE) || (nextOp == OpCodes.ENDOP))
{
int stepType = compiler.getOp(opPos);
opPos += 3;
switch (stepType)
{
case OpCodes.OP_FUNCTION :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
break;
case OpCodes.FROM_ROOT :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ROOT);
break;
case OpCodes.MATCH_ATTRIBUTE :
case OpCodes.MATCH_ANY_ANCESTOR :
case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
int tok = compiler.getOp(opPos);
opPos++;
switch (tok)
{
case OpCodes.NODETYPE_COMMENT :
targetStrings.addElement(PsuedoNames.PSEUDONAME_COMMENT);
break;
case OpCodes.NODETYPE_TEXT :
targetStrings.addElement(PsuedoNames.PSEUDONAME_TEXT);
break;
case OpCodes.NODETYPE_NODE :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
break;
case OpCodes.NODETYPE_ROOT :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ROOT);
break;
case OpCodes.NODETYPE_ANYELEMENT :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
break;
case OpCodes.NODETYPE_PI :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
break;
case OpCodes.NODENAME :
// Skip the namespace
int tokenIndex = compiler.getOp(opPos + 1);
if (tokenIndex >= 0)
{
String targetName =
(String) compiler.m_tokenQueue[tokenIndex];
if (targetName.equals("*"))
{
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
}
else
{
targetStrings.addElement(targetName);
}
}
else
{
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
}
break;
default :
targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
break;
}
break;
}
}
opPos = nextStepPos;
}
opPos = nextOpPos;
}
m_targetStrings = targetStrings;
// for(int i = 0; i < m_targetStrings.size(); i++)
// System.out.println("targetStrings["+i+"]="+m_targetStrings.elementAt(i));
}
/**
* <meta name="usage" content="advanced"/>
* This method is for building indexes of match patterns for fast lookup.
* This allows a caller to get the name or type of a node, and quickly
* find the likely candidates that may match.
*
* NEEDSDOC ($objectName$) @return
*/
public Vector getTargetElementStrings()
{
return m_targetStrings;
}
/**
* Warn the user of an problem.
*
* NEEDSDOC @param xctxt
* NEEDSDOC @param sourceNode
* NEEDSDOC @param msg
* NEEDSDOC @param args
*
* @throws javax.xml.transform.TransformerException
*/
public void warn(
XPathContext xctxt, Node sourceNode, int msg, Object[] args)
throws javax.xml.transform.TransformerException
{
String fmsg = XSLMessages.createXPATHWarning(msg, args);
ErrorListener ehandler = xctxt.getErrorListener();
if (null != ehandler)
{
// TO DO: Need to get stylesheet Locator from here.
ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
}
}
/**
* Tell the user of an assertion error, and probably throw an
* exception.
*
* NEEDSDOC @param b
* NEEDSDOC @param msg
*
* @throws javax.xml.transform.TransformerException
*/
public void assert(boolean b, String msg) throws javax.xml.transform.TransformerException
{
if (!b)
{
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.
*
* NEEDSDOC @param xctxt
* NEEDSDOC @param sourceNode
* NEEDSDOC @param msg
* NEEDSDOC @param args
*
* @throws javax.xml.transform.TransformerException
*/
public void error(
XPathContext xctxt, Node sourceNode, int msg, Object[] args)
throws javax.xml.transform.TransformerException
{
String fmsg = XSLMessages.createXPATHMessage(msg, args);
ErrorListener ehandler = xctxt.getErrorListener();
if (null != ehandler)
{
ehandler.fatalError(new TransformerException(fmsg,
(SAXSourceLocator)xctxt.getSAXLocator()));
}
else
{
SourceLocator slocator = xctxt.getSAXLocator();
System.out.println(fmsg + "; file " + slocator.getSystemId()
+ "; line " + slocator.getLineNumber() + "; column "
+ slocator.getColumnNumber());
}
}
/**
* <meta name="usage" content="advanced"/>
* The match score if no match is made.
*/
public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
/**
* <meta name="usage" content="advanced"/>
* The match score if the pattern has the form
* of a QName optionally preceded by an @ character.
*/
public static final double MATCH_SCORE_QNAME = 0.0;
/**
* <meta name="usage" content="advanced"/>
* The match score if the pattern pattern has the form NCName:*.
*/
public static final double MATCH_SCORE_NSWILD = -0.25;
/**
* <meta name="usage" content="advanced"/>
* The match score if the pattern consists of just a NodeTest.
*/
public static final double MATCH_SCORE_NODETEST = -0.5;
/**
* <meta name="usage" content="advanced"/>
* The match score if the pattern consists of something
* other than just a NodeTest or just a qname.
*/
public static final double MATCH_SCORE_OTHER = 0.5;
}