[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.
 |