[Practicalxml-commits] SF.net SVN: practicalxml:[64] trunk/src
Brought to you by:
kdgregory
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 00:42:29
|
Revision: 64 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=64&view=rev Author: kdgregory Date: 2008-12-29 00:42:26 +0000 (Mon, 29 Dec 2008) Log Message: ----------- XPathWrapper: move to xpath package Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java Removed Paths: ------------- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java Deleted: trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -1,465 +0,0 @@ -package net.sf.practicalxml; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.xml.namespace.QName; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; -import javax.xml.xpath.XPathFunction; -import javax.xml.xpath.XPathVariableResolver; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import net.sf.practicalxml.xpath.AbstractFunction; -import net.sf.practicalxml.xpath.FunctionResolver; -import net.sf.practicalxml.xpath.NamespaceResolver; - - - -/** - * This class simplifies the use of XPath expressions, hiding the factory and - * return types, and providing a simple builder-style interface for adding - * resolvers. It also maintains the expression in a compiled form, improving - * reuse performance. - */ -public class XPathWrapper -{ - private final String _expr; - private final NamespaceResolver _nsResolver = new NamespaceResolver(); - private Map<QName,Object> _variables = new HashMap<QName,Object>(); - private FunctionResolver _functions = new FunctionResolver(); - - private XPathExpression _compiled; - - - /** - * Creates a new instance, which may then be customized with various - * resolvers, and used to evaluate expressions. - */ - public XPathWrapper(String expr) - { - _expr = expr; - } - - -//---------------------------------------------------------------------------- -// Public methods -//---------------------------------------------------------------------------- - - /** - * Adds a namespace binding to this expression. All bindings must be - * added prior to the first call to <code>evaluate()</code>. - * - * @param prefix The prefix used to reference this namespace in the - * XPath expression. Note that this does <em>not</em> - * need to be the same prefix used by the document. - * @param nsURI The namespace URI to associate with this prefix. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindNamespace(String prefix, String nsURI) - { - _nsResolver.addNamespace(prefix, nsURI); - return this; - } - - - /** - * Sets the default namespace binding: this will be applied to all - * expressions that do not explicitly specify a prefix (although - * they must use the colon separating the non-existent prefix from - * the element name). - * - * @param nsURI The default namespace for this document. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindDefaultNamespace(String nsURI) - { - _nsResolver.setDefaultNamespace(nsURI); - return this; - } - - - /** - * Binds a value to a variable, replacing any previous value for that - * variable. Unlike other configuration methods, this may be called - * after calling <code>evaluate()</code>; the new values will be used - * for subsequent evaluations. - * - * @param name The name of the variable; this is turned into a - * <code>QName</code> without namespace. - * @param value The value of the variable; the XPath evaluator must - * be able to convert this value into a type usable in - * an XPath expression. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindVariable(String name, Object value) - { - return bindVariable(new QName(name), value); - } - - - /** - * Binds a value to a variable, replacing any previous value for that - * variable. Unlike other configuration methods, this may be called - * after calling <code>evaluate()</code>; the new values will be used - * for subsequent evaluations. - * - * @param name The fully-qualified name of the variable. - * @param value The value of the variable; the XPath evaluator must - * be able to convert this value into a type usable in - * an XPath expression. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindVariable(QName name, Object value) - { - _variables.put(name, value); - return this; - } - - - /** - * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this - * expression. Subsequent calls to this method with the same name will - * silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. You must bind a namespace for this function, and use - * the bound prefix to reference it in your expression. Alternatively, - * you can call the variant of <code>bindFunction()</code> that binds - * a prefix to the function's namespace. - * - * @param func The function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(AbstractFunction func) - { - _functions.addFunction(func); - return this; - } - - - /** - * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this - * expression, along with the prefix used to access that function. - * Subsequent calls to this method with the same name will silently - * replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. This method will retrieve the namespace from the - * function, and bind it to the passed prefix. - * - * @param func The function. - * @param prefix The prefix to bind to this function's namespace. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(AbstractFunction func, String prefix) - { - _functions.addFunction(func); - return bindNamespace(prefix, func.getNamespaceUri()); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling any number of arguments. Subsequent calls to this method - * with the same name will silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func) - { - return bindFunction(name, func, 0, Integer.MAX_VALUE); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling a specific number of arguments. Subsequent calls to this - * method with the same name and arity will silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * @param arity The number of arguments accepted by this function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func, int arity) - { - return bindFunction(name, func, arity, arity); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling a specific range of arguments. Subsequent calls to this - * method with the same name and range of arguments will silently - * replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * @param minArity The minimum number of arguments accepted by this - * function. - * @param maxArity The maximum number of arguments accepted by this - * function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func, - int minArity, int maxArity) - { - _functions.addFunction(func, name, minArity, maxArity); - if (!"".equals(name.getPrefix())) - bindNamespace(name.getPrefix(), name.getNamespaceURI()); - return this; - } - - - /** - * Applies this expression to the root of the specified document, - * converting the resulting NodeList into a java.util.List for ease - * in iteration. - */ - public List<Node> evaluate(Document context) - { - return evaluate(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, converting the - * resulting NodeList into a java.util.List for ease in iteration. - */ - public List<Node> evaluate(Element context) - { - compileIfNeeded(); - try - { - NodeList result = (NodeList)_compiled.evaluate(context, XPathConstants.NODESET); - List<Node> ret = new ArrayList<Node>(result.getLength()); - for (int ii = 0 ; ii < result.getLength() ; ii++) - { - ret.add(result.item(ii)); - } - return ret; - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>STRING</code> return type. - */ - public String evaluateAsString(Document context) - { - return evaluateAsString(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>STRING</code> return type. - */ - public String evaluateAsString(Element context) - { - compileIfNeeded(); - try - { - return _compiled.evaluate(context); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>NUMBER</code> return type. - */ - public Double evaluateAsNumber(Document context) - { - return evaluateAsNumber(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>NUMBER</code> return type. - */ - public Double evaluateAsNumber(Element context) - { - compileIfNeeded(); - try - { - return (Double)_compiled.evaluate(context, XPathConstants.NUMBER); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>BOOLEAN</code> return type. - */ - public Boolean evaluateAsBoolean(Document context) - { - return evaluateAsBoolean(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>BOOLEAN</code> return type. - */ - public Boolean evaluateAsBoolean(Element context) - { - compileIfNeeded(); - try - { - return (Boolean)_compiled.evaluate(context, XPathConstants.BOOLEAN); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - -//---------------------------------------------------------------------------- -// Overrides of Object -//---------------------------------------------------------------------------- - - - /** - * Two instances are considered equal if they have the same expression, - * namespace mappings, variable mappings, and function mappings. Note - * that instances with function mappings are all but guaranteed to be - * not-equal, unless you override the <code>equals()</code> method on - * the function implementation class. - */ - @Override - public final boolean equals(Object obj) - { - if (obj instanceof XPathWrapper) - { - XPathWrapper that = (XPathWrapper)obj; - return this._expr.equals(that._expr) - && this._nsResolver.equals(that._nsResolver) - && this._variables.equals(that._variables) - && this._functions.equals(that._functions); - } - return false; - } - - - /** - * Hash code is driven by the expression, ignoring differences between - * namespaces, variables, and functions. - */ - @Override - public int hashCode() - { - return _expr.hashCode(); - } - - - /** - * The string value is the expression. - */ - @Override - public String toString() - { - return _expr; - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - /** - * Compiles the expression, if it has not already been compiled. This is - * called from the various <code>evaluate</code> methods, and ensures - * that the caller has completely configured the wrapper prior to use. - */ - private void compileIfNeeded() - { - if (_compiled != null) - return; - - try - { - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(_nsResolver); - xpath.setXPathVariableResolver(new MyVariableResolver()); - xpath.setXPathFunctionResolver(_functions); - _compiled = xpath.compile(_expr); - } - catch (XPathExpressionException ee) - { - throw new XmlException("unable to compile: " + _expr, ee); - } - } - - - /** - * Resolver for variable references. - */ - private class MyVariableResolver - implements XPathVariableResolver - { - public Object resolveVariable(QName name) - { - return _variables.get(name); - } - } -} Modified: trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2008-12-29 00:42:26 UTC (rev 64) @@ -7,7 +7,7 @@ import junit.framework.Assert; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; import org.w3c.dom.Element; import org.w3c.dom.Node; Copied: trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java (from rev 63, trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java) =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -0,0 +1,463 @@ +package net.sf.practicalxml.xpath; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathVariableResolver; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import net.sf.practicalxml.XmlException; + + + +/** + * This class simplifies the use of XPath expressions, hiding the factory and + * return types, and providing a simple builder-style interface for adding + * resolvers. It also maintains the expression in a compiled form, improving + * reuse performance. + */ +public class XPathWrapper +{ + private final String _expr; + private final NamespaceResolver _nsResolver = new NamespaceResolver(); + private Map<QName,Object> _variables = new HashMap<QName,Object>(); + private FunctionResolver _functions = new FunctionResolver(); + + private XPathExpression _compiled; + + + /** + * Creates a new instance, which may then be customized with various + * resolvers, and used to evaluate expressions. + */ + public XPathWrapper(String expr) + { + _expr = expr; + } + + +//---------------------------------------------------------------------------- +// Public methods +//---------------------------------------------------------------------------- + + /** + * Adds a namespace binding to this expression. All bindings must be + * added prior to the first call to <code>evaluate()</code>. + * + * @param prefix The prefix used to reference this namespace in the + * XPath expression. Note that this does <em>not</em> + * need to be the same prefix used by the document. + * @param nsURI The namespace URI to associate with this prefix. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindNamespace(String prefix, String nsURI) + { + _nsResolver.addNamespace(prefix, nsURI); + return this; + } + + + /** + * Sets the default namespace binding: this will be applied to all + * expressions that do not explicitly specify a prefix (although + * they must use the colon separating the non-existent prefix from + * the element name). + * + * @param nsURI The default namespace for this document. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindDefaultNamespace(String nsURI) + { + _nsResolver.setDefaultNamespace(nsURI); + return this; + } + + + /** + * Binds a value to a variable, replacing any previous value for that + * variable. Unlike other configuration methods, this may be called + * after calling <code>evaluate()</code>; the new values will be used + * for subsequent evaluations. + * + * @param name The name of the variable; this is turned into a + * <code>QName</code> without namespace. + * @param value The value of the variable; the XPath evaluator must + * be able to convert this value into a type usable in + * an XPath expression. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindVariable(String name, Object value) + { + return bindVariable(new QName(name), value); + } + + + /** + * Binds a value to a variable, replacing any previous value for that + * variable. Unlike other configuration methods, this may be called + * after calling <code>evaluate()</code>; the new values will be used + * for subsequent evaluations. + * + * @param name The fully-qualified name of the variable. + * @param value The value of the variable; the XPath evaluator must + * be able to convert this value into a type usable in + * an XPath expression. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindVariable(QName name, Object value) + { + _variables.put(name, value); + return this; + } + + + /** + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression. Subsequent calls to this method with the same name will + * silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. You must bind a namespace for this function, and use + * the bound prefix to reference it in your expression. Alternatively, + * you can call the variant of <code>bindFunction()</code> that binds + * a prefix to the function's namespace. + * + * @param func The function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func) + { + _functions.addFunction(func); + return this; + } + + + /** + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression, along with the prefix used to access that function. + * Subsequent calls to this method with the same name will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. This method will retrieve the namespace from the + * function, and bind it to the passed prefix. + * + * @param func The function. + * @param prefix The prefix to bind to this function's namespace. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func, String prefix) + { + _functions.addFunction(func); + return bindNamespace(prefix, func.getNamespaceUri()); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling any number of arguments. Subsequent calls to this method + * with the same name will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func) + { + return bindFunction(name, func, 0, Integer.MAX_VALUE); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific number of arguments. Subsequent calls to this + * method with the same name and arity will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param arity The number of arguments accepted by this function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, int arity) + { + return bindFunction(name, func, arity, arity); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific range of arguments. Subsequent calls to this + * method with the same name and range of arguments will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param minArity The minimum number of arguments accepted by this + * function. + * @param maxArity The maximum number of arguments accepted by this + * function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, + int minArity, int maxArity) + { + _functions.addFunction(func, name, minArity, maxArity); + if (!"".equals(name.getPrefix())) + bindNamespace(name.getPrefix(), name.getNamespaceURI()); + return this; + } + + + /** + * Applies this expression to the root of the specified document, + * converting the resulting NodeList into a java.util.List for ease + * in iteration. + */ + public List<Node> evaluate(Document context) + { + return evaluate(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, converting the + * resulting NodeList into a java.util.List for ease in iteration. + */ + public List<Node> evaluate(Element context) + { + compileIfNeeded(); + try + { + NodeList result = (NodeList)_compiled.evaluate(context, XPathConstants.NODESET); + List<Node> ret = new ArrayList<Node>(result.getLength()); + for (int ii = 0 ; ii < result.getLength() ; ii++) + { + ret.add(result.item(ii)); + } + return ret; + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>STRING</code> return type. + */ + public String evaluateAsString(Document context) + { + return evaluateAsString(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>STRING</code> return type. + */ + public String evaluateAsString(Element context) + { + compileIfNeeded(); + try + { + return _compiled.evaluate(context); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>NUMBER</code> return type. + */ + public Double evaluateAsNumber(Document context) + { + return evaluateAsNumber(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>NUMBER</code> return type. + */ + public Double evaluateAsNumber(Element context) + { + compileIfNeeded(); + try + { + return (Double)_compiled.evaluate(context, XPathConstants.NUMBER); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>BOOLEAN</code> return type. + */ + public Boolean evaluateAsBoolean(Document context) + { + return evaluateAsBoolean(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>BOOLEAN</code> return type. + */ + public Boolean evaluateAsBoolean(Element context) + { + compileIfNeeded(); + try + { + return (Boolean)_compiled.evaluate(context, XPathConstants.BOOLEAN); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + +//---------------------------------------------------------------------------- +// Overrides of Object +//---------------------------------------------------------------------------- + + + /** + * Two instances are considered equal if they have the same expression, + * namespace mappings, variable mappings, and function mappings. Note + * that instances with function mappings are all but guaranteed to be + * not-equal, unless you override the <code>equals()</code> method on + * the function implementation class. + */ + @Override + public final boolean equals(Object obj) + { + if (obj instanceof XPathWrapper) + { + XPathWrapper that = (XPathWrapper)obj; + return this._expr.equals(that._expr) + && this._nsResolver.equals(that._nsResolver) + && this._variables.equals(that._variables) + && this._functions.equals(that._functions); + } + return false; + } + + + /** + * Hash code is driven by the expression, ignoring differences between + * namespaces, variables, and functions. + */ + @Override + public int hashCode() + { + return _expr.hashCode(); + } + + + /** + * The string value is the expression. + */ + @Override + public String toString() + { + return _expr; + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Compiles the expression, if it has not already been compiled. This is + * called from the various <code>evaluate</code> methods, and ensures + * that the caller has completely configured the wrapper prior to use. + */ + private void compileIfNeeded() + { + if (_compiled != null) + return; + + try + { + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(_nsResolver); + xpath.setXPathVariableResolver(new MyVariableResolver()); + xpath.setXPathFunctionResolver(_functions); + _compiled = xpath.compile(_expr); + } + catch (XPathExpressionException ee) + { + throw new XmlException("unable to compile: " + _expr, ee); + } + } + + + /** + * Resolver for variable references. + */ + private class MyVariableResolver + implements XPathVariableResolver + { + public Object resolveVariable(QName name) + { + return _variables.get(name); + } + } +} Deleted: trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -1,339 +0,0 @@ -package net.sf.practicalxml; - -import java.util.List; -import javax.xml.namespace.QName; -import javax.xml.xpath.XPathFunction; -import javax.xml.xpath.XPathFunctionException; - -import net.sf.practicalxml.xpath.AbstractFunction; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - - -public class TestXPathWrapper -extends AbstractTestCase -{ - public TestXPathWrapper(String name) - { - super(name); - } - - -//---------------------------------------------------------------------------- -// Test data -//---------------------------------------------------------------------------- - - public final static String EL_ROOT = "root"; - public final static String EL_CHILD = "child"; - public final static String NS1 = "ns1"; - public final static String NS2 = "ns2"; - - Document _dom; - Element _root; - Element _child1; - Element _child2; - Element _child3; - - @Override - protected void setUp() - { - _root = DomUtil.newDocument(EL_ROOT); - _child1 = DomUtil.appendChild(_root, EL_CHILD); - _child2 = DomUtil.appendChild(_root, NS1, EL_CHILD); - _child3 = DomUtil.appendChild(_root, NS2, EL_CHILD); - _dom = _root.getOwnerDocument(); - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - - /** - * A standard XPath function implementation that returns the namespace - * of the first selected node. - */ - private static class MyStandardFunction - implements XPathFunction - { - public Object evaluate(List args) - throws XPathFunctionException - { - NodeList arg = (NodeList)args.get(0); - return arg.item(0).getNamespaceURI(); - } - } - - - /** - * An <code>AbstractFunction</code> implementation that returns the - * namespace of the first selected node. - */ - private static class MyAbstractFunction - extends AbstractFunction<String> - { - public MyAbstractFunction(String nsUri, String name) - { - super(nsUri, name); - } - - @Override - protected String processArg(int index, Node value, String helper) - throws Exception - { - return value.getNamespaceURI(); - } - } - -//---------------------------------------------------------------------------- -// Test Cases -//---------------------------------------------------------------------------- - - // the basic test to verify we can compile and execute - public void testCurrentElement() - throws Exception - { - XPathWrapper xpath = new XPathWrapper("."); - - List<Node> result1 = xpath.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_root, result1.get(0)); - - List<Node> result2 = xpath.evaluate(_root); - assertEquals(1, result2.size()); - assertSame(_root, result2.get(0)); - - List<Node> result3 = xpath.evaluate(_child1); - assertEquals(1, result3.size()); - assertSame(_child1, result3.get(0)); - } - - - public void testEvalAsString() - throws Exception - { - _root.setAttribute("foo", "bar"); - _root.setAttribute("argle", "bargle"); - - XPathWrapper xpath = new XPathWrapper("@foo"); - - assertEquals("bar", xpath.evaluateAsString(_root)); - assertEquals("bar", xpath.evaluateAsString(_dom)); - } - - - public void testEvalAsNumber() - throws Exception - { - _root.setAttribute("foo", "10"); - - XPathWrapper xpath = new XPathWrapper("@foo"); - - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_root)); - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_dom)); - } - - - public void testEvalAsBoolean() - throws Exception - { - _root.setAttribute("foo", "10"); - - XPathWrapper xpath1 = new XPathWrapper("@foo=10"); - - assertTrue(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertTrue(xpath1.evaluateAsBoolean(_dom).booleanValue()); - - _root.setAttribute("foo", "20"); - - assertFalse(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertFalse(xpath1.evaluateAsBoolean(_dom).booleanValue()); - } - - - public void testNamespaces() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("//child"); - List<Node> result1 = xpath1.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_child1, result1.get(0)); - - XPathWrapper xpath2 = new XPathWrapper("//ns:child") - .bindNamespace("ns", NS1); - List<Node> result2 = xpath2.evaluate(_dom); - assertEquals(1, result2.size()); - assertSame(_child2, result2.get(0)); - - XPathWrapper xpath3 = new XPathWrapper("//:child") - .bindDefaultNamespace(NS2); - List<Node> result3 = xpath3.evaluate(_dom); - assertEquals(1, result3.size()); - assertSame(_child3, result3.get(0)); - } - - - public void testVariables() throws Exception - { - _child1.setAttribute("bar", "baz"); - _child2.setAttribute("bar", "bargle"); - - XPathWrapper xpath = new XPathWrapper("//*[@bar=$test]") - .bindVariable(new QName("test"), "baz"); - - List<Node> result1 = xpath.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_child1, result1.get(0)); - - xpath.bindVariable("test", "bargle"); - - List<Node> result2 = xpath.evaluate(_dom); - assertEquals(1, result2.size()); - assertSame(_child2, result2.get(0)); - } - - - public void testAbstractFunctions() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") - .bindNamespace("ns", NS1) - .bindFunction(new MyAbstractFunction(NS1, "myfunc")); - - assertEquals("", xpath1.evaluateAsString(_child1)); - assertEquals(NS1, xpath1.evaluateAsString(_child2)); - - XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.)") - .bindFunction(new MyAbstractFunction(NS1, "myfunc"), "ns"); - - assertEquals("", xpath2.evaluateAsString(_child1)); - assertEquals(NS1, xpath2.evaluateAsString(_child2)); - } - - - public void testStandardFunctions() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") - .bindFunction(new QName(NS1, "myfunc", "ns"), - new MyStandardFunction()); - - assertEquals("", xpath1.evaluateAsString(_child1)); - assertEquals(NS1, xpath1.evaluateAsString(_child2)); - - XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.,.)") - .bindFunction(new QName(NS2, "myfunc", "ns"), - new MyStandardFunction(), - 2); - - assertEquals("", xpath2.evaluateAsString(_child1)); - assertEquals(NS1, xpath2.evaluateAsString(_child2)); - } - - - public void testUnresolvableFunction() throws Exception - { - // we call with two arguments, it only gets resolved for one - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.,.)") - .bindFunction(new QName(NS2, "myfunc", "ns"), - new MyStandardFunction(), - 1); - try - { - xpath1.evaluateAsString(_child1); - fail("didn't throw even though arity was wrong"); - } - catch (XmlException ee) - { - // success - } - } - - - - public void testEqualsAndHashCode() throws Exception - { - Object obj1a = new XPathWrapper("//foo"); - Object obj1b = new XPathWrapper("//foo"); - Object obj2a = new XPathWrapper("//foo") - .bindDefaultNamespace("zippy"); - Object obj2b = new XPathWrapper("//foo") - .bindDefaultNamespace("zippy"); - Object obj3a = new XPathWrapper("//foo") - .bindNamespace("argle", "bargle"); - Object obj3b = new XPathWrapper("//foo") - .bindNamespace("argle", "bargle"); - Object obj4a = new XPathWrapper("//foo") - .bindVariable("argle", "bargle"); - Object obj4b = new XPathWrapper("//foo") - .bindVariable("argle", "bargle"); - Object obj5a = new XPathWrapper("//foo") - .bindFunction(new QName("foo"), null); - Object obj5b = new XPathWrapper("//foo") - .bindFunction(new QName("foo"), null); - - assertFalse(obj1a.equals(null)); - assertFalse(obj1a.equals(new Object())); - - assertTrue(obj1a.equals(obj1b)); - assertTrue(obj1b.equals(obj1a)); - assertEquals(obj1a.hashCode(), obj1b.hashCode()); - - assertFalse(obj1a.equals(obj2a)); - assertTrue(obj2a.equals(obj2b)); - assertTrue(obj2b.equals(obj2a)); - assertEquals(obj1a.hashCode(), obj2a.hashCode()); - assertEquals(obj2a.hashCode(), obj2b.hashCode()); - - assertFalse(obj1a.equals(obj3a)); - assertTrue(obj3a.equals(obj3b)); - assertTrue(obj3b.equals(obj3a)); - assertEquals(obj1a.hashCode(), obj3a.hashCode()); - assertEquals(obj3a.hashCode(), obj3b.hashCode()); - - assertFalse(obj1a.equals(obj4a)); - assertTrue(obj4a.equals(obj4b)); - assertTrue(obj4b.equals(obj4a)); - assertEquals(obj1a.hashCode(), obj4a.hashCode()); - assertEquals(obj4a.hashCode(), obj4b.hashCode()); - - assertFalse(obj1a.equals(obj5a)); - assertTrue(obj5a.equals(obj5b)); - assertTrue(obj5b.equals(obj5a)); - assertEquals(obj1a.hashCode(), obj5a.hashCode()); - assertEquals(obj5a.hashCode(), obj5b.hashCode()); - } - - - public void testToString() throws Exception - { - final String expr = "//foo"; - assertEquals(expr, new XPathWrapper(expr).toString()); - assertEquals(expr, new XPathWrapper(expr).bindNamespace("foo", "bar").toString()); - } - - - public void testFailures() throws Exception - { - try - { - new XPathWrapper(".foo.").evaluate(_dom); - fail("compiled invalid expression"); - } - catch (XmlException ee) - { - // success - } - - try - { - new XPathWrapper("@foo=$bar").evaluate(_dom); - fail("evaluated expression with unbound variable"); - } - catch (XmlException ee) - { - // success - } - } - -} Modified: trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java 2008-12-29 00:42:26 UTC (rev 64) @@ -7,7 +7,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestDomAsserts Copied: trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java (from rev 63, trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java) =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -0,0 +1,343 @@ +package net.sf.practicalxml.xpath; + +import java.util.List; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathFunctionException; + +import net.sf.practicalxml.AbstractTestCase; +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlException; +import net.sf.practicalxml.xpath.AbstractFunction; +import net.sf.practicalxml.xpath.XPathWrapper; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +public class TestXPathWrapper +extends AbstractTestCase +{ + public TestXPathWrapper(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Test data +//---------------------------------------------------------------------------- + + public final static String EL_ROOT = "root"; + public final static String EL_CHILD = "child"; + public final static String NS1 = "ns1"; + public final static String NS2 = "ns2"; + + Document _dom; + Element _root; + Element _child1; + Element _child2; + Element _child3; + + @Override + protected void setUp() + { + _root = DomUtil.newDocument(EL_ROOT); + _child1 = DomUtil.appendChild(_root, EL_CHILD); + _child2 = DomUtil.appendChild(_root, NS1, EL_CHILD); + _child3 = DomUtil.appendChild(_root, NS2, EL_CHILD); + _dom = _root.getOwnerDocument(); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + /** + * A standard XPath function implementation that returns the namespace + * of the first selected node. + */ + private static class MyStandardFunction + implements XPathFunction + { + public Object evaluate(List args) + throws XPathFunctionException + { + NodeList arg = (NodeList)args.get(0); + return arg.item(0).getNamespaceURI(); + } + } + + + /** + * An <code>AbstractFunction</code> implementation that returns the + * namespace of the first selected node. + */ + private static class MyAbstractFunction + extends AbstractFunction<String> + { + public MyAbstractFunction(String nsUri, String name) + { + super(nsUri, name); + } + + @Override + protected String processArg(int index, Node value, String helper) + throws Exception + { + return value.getNamespaceURI(); + } + } + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + // the basic test to verify we can compile and execute + public void testCurrentElement() + throws Exception + { + XPathWrapper xpath = new XPathWrapper("."); + + List<Node> result1 = xpath.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_root, result1.get(0)); + + List<Node> result2 = xpath.evaluate(_root); + assertEquals(1, result2.size()); + assertSame(_root, result2.get(0)); + + List<Node> result3 = xpath.evaluate(_child1); + assertEquals(1, result3.size()); + assertSame(_child1, result3.get(0)); + } + + + public void testEvalAsString() + throws Exception + { + _root.setAttribute("foo", "bar"); + _root.setAttribute("argle", "bargle"); + + XPathWrapper xpath = new XPathWrapper("@foo"); + + assertEquals("bar", xpath.evaluateAsString(_root)); + assertEquals("bar", xpath.evaluateAsString(_dom)); + } + + + public void testEvalAsNumber() + throws Exception + { + _root.setAttribute("foo", "10"); + + XPathWrapper xpath = new XPathWrapper("@foo"); + + assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_root)); + assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_dom)); + } + + + public void testEvalAsBoolean() + throws Exception + { + _root.setAttribute("foo", "10"); + + XPathWrapper xpath1 = new XPathWrapper("@foo=10"); + + assertTrue(xpath1.evaluateAsBoolean(_root).booleanValue()); + assertTrue(xpath1.evaluateAsBoolean(_dom).booleanValue()); + + _root.setAttribute("foo", "20"); + + assertFalse(xpath1.evaluateAsBoolean(_root).booleanValue()); + assertFalse(xpath1.evaluateAsBoolean(_dom).booleanValue()); + } + + + public void testNamespaces() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("//child"); + List<Node> result1 = xpath1.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_child1, result1.get(0)); + + XPathWrapper xpath2 = new XPathWrapper("//ns:child") + .bindNamespace("ns", NS1); + List<Node> result2 = xpath2.evaluate(_dom); + assertEquals(1, result2.size()); + assertSame(_child2, result2.get(0)); + + XPathWrapper xpath3 = new XPathWrapper("//:child") + .bindDefaultNamespace(NS2); + List<Node> result3 = xpath3.evaluate(_dom); + assertEquals(1, result3.size()); + assertSame(_child3, result3.get(0)); + } + + + public void testVariables() throws Exception + { + _child1.setAttribute("bar", "baz"); + _child2.setAttribute("bar", "bargle"); + + XPathWrapper xpath = new XPathWrapper("//*[@bar=$test]") + .bindVariable(new QName("test"), "baz"); + + List<Node> result1 = xpath.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_child1, result1.get(0)); + + xpath.bindVariable("test", "bargle"); + + List<Node> result2 = xpath.evaluate(_dom); + assertEquals(1, result2.size()); + assertSame(_child2, result2.get(0)); + } + + + public void testAbstractFunctions() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindNamespace("ns", NS1) + .bindFunction(new MyAbstractFunction(NS1, "myfunc")); + + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); + + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new MyAbstractFunction(NS1, "myfunc"), "ns"); + + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); + } + + + public void testStandardFunctions() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new QName(NS1, "myfunc", "ns"), + new MyStandardFunction()); + + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); + + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 2); + + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); + } + + + public void testUnresolvableFunction() throws Exception + { + // we call with two arguments, it only gets resolved for one + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 1); + try + { + xpath1.evaluateAsString(_child1); + fail("didn't throw even though arity was wrong"); + } + catch (XmlException ee) + { + // success + } + } + + + + public void testEqualsAndHashCode() throws Exception + { + Object obj1a = new XPathWrapper("//foo"); + Object obj1b = new XPathWrapper("//foo"); + Object obj2a = new XPathWrapper("//foo") + .bindDefaultNamespace("zippy"); + Object obj2b = new XPathWrapper("//foo") + .bindDefaultNamespace("zippy"); + Object obj3a = new XPathWrapper("//foo") + .bindNamespace("argle", "bargle"); + Object obj3b = new XPathWrapper("//foo") + .bindNamespace("argle", "bargle"); + Object obj4a = new XPathWrapper("//foo") + .bindVariable("argle", "bargle"); + Object obj4b = new XPathWrapper("//foo") + .bindVariable("argle", "bargle"); + Object obj5a = new XPathWrapper("//foo") + .bindFunction(new QName("foo"), null); + Object obj5b = new XPathWrapper("//foo") + .bindFunction(new QName("foo"), null); + + assertFalse(obj1a.equals(null)); + assertFalse(obj1a.equals(new Object())); + + assertTrue(obj1a.equals(obj1b)); + assertTrue(obj1b.equals(obj1a)); + assertEquals(obj1a.hashCode(), obj1b.hashCode()); + + assertFalse(obj1a.equals(obj2a)); + assertTrue(obj2a.equals(obj2b)); + assertTrue(obj2b.equals(obj2a)); + assertEquals(obj1a.hashCode(), obj2a.hashCode()); + assertEquals(obj2a.hashCode(), obj2b.hashCode()); + + assertFalse(obj1a.equals(obj3a)); + assertTrue(obj3a.equals(obj3b)); + assertTrue(obj3b.equals(obj3a)); + assertEquals(obj1a.hashCode(), obj3a.hashCode()); + assertEquals(obj3a.hashCode(), obj3b.hashCode()); + + assertFalse(obj1a.equals(obj4a)); + assertTrue(obj4a.equals(obj4b)); + assertTrue(obj4b.equals(obj4a)); + assertEquals(obj1a.hashCode(), obj4a.hashCode()); + assertEquals(obj4a.hashCode(), obj4b.hashCode()); + + assertFalse(obj1a.equals(obj5a)); + assertTrue(obj5a.equals(obj5b)); + assertTrue(obj5b.equals(obj5a)); + assertEquals(obj1a.hashCode(), obj5a.hashCode()); + assertEquals(obj5a.hashCode(), obj5b.hashCode()); + } + + + public void testToString() throws Exception + { + final String expr = "//foo"; + assertEquals(expr, new XPathWrapper(expr).toString()); + assertEquals(expr, new XPathWrapper(expr).bindNamespace("foo", "bar").toString()); + } + + + public void testFailures() throws Exception + { + try + { + new XPathWrapper(".foo.").evaluate(_dom); + fail("compiled invalid expression"); + } + catch (XmlException ee) + { + // success + } + + try + { + new XPathWrapper("@foo=$bar").evaluate(_dom); + fail("evaluated expression with unbound variable"); + } + catch (XmlException ee) + { + // success + } + } + +} Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-29 00:42:26 UTC (rev 64) @@ -10,7 +10,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestLowercase Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-29 00:42:26 UTC (rev 64) @@ -10,7 +10,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestUppercase This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |