You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by dm...@apache.org on 2002/04/10 05:40:22 UTC

cvs commit: jakarta-commons/jxpath/xdocs/stylesheets project.xml

dmitri      02/04/09 20:40:21

  Modified:    jxpath   build.xml
               jxpath/src/java/org/apache/commons/jxpath
                        ClassFunctions.java ExpressionContext.java
                        Function.java JXPath.java JXPathContext.java
                        PackageFunctions.java
               jxpath/src/java/org/apache/commons/jxpath/functions
                        ConstructorFunction.java MethodFunction.java
                        Types.java
               jxpath/src/java/org/apache/commons/jxpath/ri
                        EvalContext.java JXPathContextReferenceImpl.java
                        Parser.java
               jxpath/src/java/org/apache/commons/jxpath/ri/axes
                        AncestorContext.java AttributeContext.java
                        ChildContext.java DescendantContext.java
                        InitialContext.java NamespaceContext.java
                        ParentContext.java PrecedingOrFollowingContext.java
                        PredicateContext.java RootContext.java
                        SelfContext.java UnionContext.java
               jxpath/src/java/org/apache/commons/jxpath/ri/compiler
                        ExpressionPath.java ExtensionFunction.java
                        LocationPath.java Path.java
               jxpath/src/java/org/apache/commons/jxpath/ri/pointers
                        BeanPropertyPointer.java ContainerPointer.java
                        DOMAttributePointer.java DOMNodePointer.java
                        DynamicPropertyPointer.java NodePointer.java
                        NullPointer.java NullPropertyPointer.java
                        PropertyAccessHelper.java PropertyPointer.java
                        VariablePointer.java
               jxpath/src/test/org/apache/commons/jxpath
                        JXPathTestCase.java NestedTestBean.java
                        TestBean.java TestFunctions.java
  Added:       jxpath/src/java/org/apache/commons/jxpath
                        AbstractFactory.java
               jxpath/src/java/org/apache/commons/jxpath/ri/pointers
                        BeanPointerFactory.java
                        ContainerPointerFactory.java DOMPointerFactory.java
                        DynamicPointerFactory.java NodePointerFactory.java
                        NullElementPointer.java
               jxpath/src/test/org/apache/commons/jxpath TestFactory.java
               jxpath/xdocs contributors.xml index.xml users-guide.xml
               jxpath/xdocs/images jakarta-logo.gif logo.jpg
               jxpath/xdocs/stylesheets project.xml
  Log:
  Multiple changes
  
  Revision  Changes    Path
  1.4       +77 -2     jakarta-commons/jxpath/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/build.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- build.xml	11 Sep 2001 23:34:26 -0000	1.3
  +++ build.xml	10 Apr 2002 03:40:19 -0000	1.4
  @@ -3,7 +3,7 @@
   
   <!--
           "JXPath" component of the Jakarta Commons Subproject
  -        $Id: build.xml,v 1.3 2001/09/11 23:34:26 dmitri Exp $
  +        $Id: build.xml,v 1.4 2002/04/10 03:40:19 dmitri Exp $
   -->
   
   
  @@ -43,6 +43,10 @@
     <!-- The base directory for unit test sources -->
     <property name="test.home"               value="src/test"/>
   
  +  <!-- Anakia props -->
  +  <property name="docs.src" value="./xdocs"/>
  +  <property name="docs.dest" value="${dist.home}/docs"/>
  +
   
   <!-- ========== Compiler Defaults ========================================= -->
   
  @@ -277,7 +281,7 @@
     <!-- D I S T                                                            -->
     <!-- ================================================================== -->
   
  -  <target name="dist" depends="compile,jar,javadoc"
  +  <target name="dist" depends="compile,jar,xdoc,javadoc"
      description="Create binary distribution">
       <mkdir      dir="${dist.home}"/>
       <copy      file="../LICENSE"
  @@ -300,4 +304,75 @@
       </java>
     </target>
   
  +
  +<!-- ========== Documentation ========================================= -->
  +
  +  <target name="xdoc.fetch-stylesheet" unless="localstylesheet">
  +      <echo>
  +       ####################################################################
  +       #
  +       #  Fetching the latest stylesheet from jakarta-site2
  +       #
  +       #  NOTE : As this build target is meant for developers, this requires
  +       #    a properly setup CVS.  But you are encouraged to use this to
  +       #    experiment with Anakia - if the fetch fails, it may be because
  +       #    you haven't yet logged into CVS. The way to do it, assuming you
  +       #    have a resonable CVS client setup is
  +       #
  +       #  $ cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
  +       #  password: anoncvs
  +       #
  +       #  and that should solve it.
  +       #
  +       #  See http://jakarta.apache.org/site/cvsindex.html for more
  +       #      information, or http://www.cvshome.org/
  +       #
  +       #  Ant really is the bee's knees. http://jakarta.apache.org/ant/
  +       #
  +       ######################################################################
  +       </echo>
  +
  +      <cvs cvsRoot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
  +          command="checkout -p jakarta-site2/xdocs/stylesheets/site.vsl"
  +          output="${docs.src}/stylesheets/site.vsl"
  +      />
  +  </target>
  +
  +  <target name="xdoc" depends="xdoc.fetch-stylesheet"
  +          description="Generates HTML documentation from XML source">
  +    <mkdir      dir="${dist.home}"/>
  +    <mkdir      dir="${dist.home}/docs"/>
  +
  +      <taskdef name="anakia" classname="org.apache.velocity.anakia.AnakiaTask">
  +          <classpath location="${velocity.jar}"/>
  +          <classpath location="${jdom.jar}"/>
  +          <classpath location="${xerces.jar}"/>
  +      </taskdef>
  +
  +      <echo>
  +       #######################################################
  +       #
  +       #  Now using Anakia to transform our XML documentation
  +       #  to HTML.
  +       #
  +       #######################################################
  +       </echo>
  +
  +      <anakia basedir="${docs.src}" destdir="${docs.dest}/"
  +           extension=".html" style="./site.vsl"
  +           projectFile="stylesheets/project.xml"
  +           excludes="**/stylesheets/** empty.xml"
  +           includes="**/*.xml"
  +           lastModifiedCheck="true"
  +           templatePath="xdocs/stylesheets">
  +      </anakia>
  +
  +      <copy todir="${docs.dest}/images" filtering="no">
  +          <fileset dir="${docs.src}/images">
  +              <include name="**/*.gif"/>
  +              <include name="**/*.jpeg"/>
  +              <include name="**/*.jpg"/>
  +          </fileset>
  +      </copy>
  +  </target>
   </project>
  
  
  
  1.2       +8 -3      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ClassFunctions.java
  
  Index: ClassFunctions.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ClassFunctions.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ClassFunctions.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ ClassFunctions.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ClassFunctions.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ClassFunctions.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -83,8 +83,13 @@
    *  <dd>Equivalent to <code>new Integer(4).floatValue()</code></dd>
    * </dl>
    *
  + * <p>
  + * If the first argument of a method is ExpressionContext, the
  + * expression context in which the function is evaluated is passed to
  + * the method.
  + *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class ClassFunctions implements Functions {
       private Class functionClass;
  
  
  
  1.2       +18 -7     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ExpressionContext.java
  
  Index: ExpressionContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ExpressionContext.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExpressionContext.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ ExpressionContext.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ExpressionContext.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ExpressionContext.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -66,8 +66,7 @@
   
   /**
    * If an extenstion function has an argument of type ExpressionContext,
  - * it can gain access to the current node or all nodes of
  - * an XPath expression context.
  + * it can gain access to the current node of an XPath expression context.
    * <p>
    * Example:
    * <blockquote><pre>
  @@ -77,7 +76,7 @@
    *       if (value == null){
    *           return "null";
    *       }
  - *       return value.getClass();
  + *       return value.getClass().getName();
    *    }
    * }
    * </pre></blockquote>
  @@ -85,13 +84,20 @@
    * You can then register this extension function using a {@link ClassFunctions
    * ClassFunctions} object and call it like this:
    * <blockquote><pre>
  - *   "/descendent-or-self::node()[ns:objectType(.) = 'java.util.Date']"
  + *   "/descendent-or-self::node()[ns:objectType() = 'java.util.Date']"
    * </pre></blockquote>
    * This expression will find all nodes of the graph that are dates.
    */
   public interface ExpressionContext
   {
       /**
  +     * Get the JXPathContext in which this function is being evaluated.
  +     *
  +     * @return A list representing the current context nodes.
  +     */
  +    public JXPathContext getJXPathContext();
  +
  +    /**
        * Get the current context node.
        *
        * @return The current context node pointer.
  @@ -105,4 +111,9 @@
        * @return A list representing the current context nodes.
        */
       public List getContextNodeList();
  +
  +    /**
  +     * Returns the current context position.
  +     */
  +    public int getPosition();
   }
  
  
  
  1.2       +7 -4      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/Function.java
  
  Index: Function.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/Function.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Function.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ Function.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/Function.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/Function.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,7 +74,7 @@
    * See {@link ClassFunctions ClassFunctions} and {@link PackageFunctions PackageFunctions}.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public interface Function {
   
  @@ -82,6 +82,9 @@
        * Computes the value of the function. Each implementation of Function
        * is responsible for conversion of supplied parameters to the required
        * argument types.
  +     *
  +     * @param context can be used to acquire the context in which the
  +     *    function is being evaluted.
        */
  -    Object invoke(Object[] parameters);
  +    Object invoke(ExpressionContext context, Object[] parameters);
   }
  
  
  
  1.2       +5 -5      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPath.java
  
  Index: JXPath.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPath.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JXPath.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ JXPath.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPath.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPath.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -70,17 +70,17 @@
    * request to it. Use JXPathContext APIs instead of JXPath APIs if any of
    * the following requirements exist:
    * <ul>
  - * <li>There is a need to evaluate multiple XPaths over the same object graph.
  - * JXPathContext is optimized for that.
    * <li>There is a need for the support of variables.  JXPathContext has a method
    * that allows registering of a pool of variables.
    * <li>There is a need to use extension functions other than
    * Java method calls using the default syntax (see {@link PackageFunctions
    * PackageFunctions}
  + * <li>There is a need to use an AbstractFactory, which can create new objects.
  + * <li>There is a need to use a hierarchy of evaluation contexts.
    * </ul>
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public final class JXPath {
   
  
  
  
  1.5       +70 -15    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPathContext.java
  
  Index: JXPathContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPathContext.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- JXPathContext.java	26 Sep 2001 23:37:39 -0000	1.4
  +++ JXPathContext.java	10 Apr 2002 03:40:19 -0000	1.5
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPathContext.java,v 1.4 2001/09/26 23:37:39 dmitri Exp $
  - * $Revision: 1.4 $
  - * $Date: 2001/09/26 23:37:39 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/JXPathContext.java,v 1.5 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.5 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -157,9 +157,6 @@
    * A collection can be an arbitrary array or an instance of java.util.Collection.
    * <p>
    * Note: in XPath the first element of a collection has index 1, not 0.<br>
  - * Note: The root node of the context can be a collection too. If you want
  - * to follow the standard XPath syntax then instead of using the ".[3]" syntax
  - * you should use "self::node()[3]".
    *
    * <h3>Example 4: Map Element Access</h3>
    *
  @@ -216,7 +213,7 @@
    * ...
    *
    * JXPathContext context = JXPathContext.newContext(auth);
  - * List threeBooks = (List)context.eval("books[position() < 4]");
  + * List threeBooks = (List)context.eval("books[position() &lt; 4]");
    * </blockquote></pre>
    *
    * This returns a list of at most three books from the array of all books
  @@ -246,7 +243,31 @@
    *
    * </blockquote></pre>
    *
  - * <h3>Example 7: Using Variables</h3>
  + * <h3>Example 7: Creating objects</h3>
  + * JXPath can be used to create new objects. First, create a subclass of
  + * {@link AbstractFactory AbstractFactory} and install it on the JXPathContext.
  + * Then call {@link JXPathContext#createPath createPath()} instead of "setValue".
  + * JXPathContext will invoke your AbstractFactory when it discovers that an
  + * intermediate node of the path is <b>null</b>.  It will not override existing
  + * nodes.
  + *
  + * <pre><blockquote>
  + * public class AddressFactory extends AbstractFactory {
  + *    public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index){
  + *     if ((parent instanceof Employee) &amp;&amp; name.equals("address"){
  + *       ((Employee)parent).setAddress(new Address());
  + *       return true;
  + *     }
  + *     return false;
  + *   }
  + * }
  + *
  + * JXPathContext context = JXPathContext.newContext(emp);
  + * context.setFactory(new AddressFactory());
  + * context.createPath("address/zipCode", "90190");
  + * </blockquote></pre>
  + *
  + * <h3>Example 8: Using Variables</h3>
    * JXPath supports the notion of variables. The XPath syntax for accessing
    * variables is <i>"$varName"</i>.
    *
  @@ -290,7 +311,7 @@
    * String title = (String)context.getValue("$books[2]/title);
    * </blockquote></pre>
    *
  - * <h3>Example 8: Using Nested Contexts</h3>
  + * <h3>Example 9: Using Nested Contexts</h3>
    * If you need to use the same set of variable while interpreting
    * XPaths with different beans, it makes sense to put the variables in a separate
    * context and specify that context as a parent context every time you
  @@ -334,7 +355,7 @@
    * As you can see, the target of the method is specified as the first parameter
    * of the function.
    *
  - * <h3>Example 10: Using Custom Extension Functions</h3>
  + * <h3>Example 11: Using Custom Extension Functions</h3>
    * Collections of custom extension functions can be implemented
    * as {@link Functions Functions} objects or as Java classes, whose methods
    * become extenstion functions.
  @@ -373,24 +394,26 @@
    *
    * <h2>Notes</h2>
    * <ul>
  - * <li>The current version of JXPath does not support DOM attributes. Even though XPaths
  + * <li>JXPath does not support DOM attributes for non-DOM objects. Even though XPaths
    *     like "para[@type='warning']" are legitimate, they will always produce empty results.
  - *     This may change in future versions of XPath: the related trade-offs are currently
  - *     being evaluated.
  + *     The only attribute supported for JavaBeans is "name".  The XPath "foo/bar" is
  + *     equivalent to "foo[@name='bar']".
    * <li>The current version of JXPath does not support the <code>id(string)</code>
    *     and <code>key(key, value)</code> XPath functions.
    * </ul>
    *
  - * See <a href="http://www.w3.org/TR/xpath">XML Path Language (XPath) Version 1.0 </a>
  + * See <a href="http://www.w3schools.com/xpath">XPath Tutorial by W3Schools</a><br>
  + * Also see <a href="http://www.w3.org/TR/xpath">XML Path Language (XPath) Version 1.0 </a>
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.4 $ $Date: 2001/09/26 23:37:39 $
  + * @version $Revision: 1.5 $ $Date: 2002/04/10 03:40:19 $
    */
   public abstract class JXPathContext {
       protected JXPathContext parentContext;
       protected Object contextBean;
       protected Variables vars;
       protected Functions functions;
  +    protected AbstractFactory factory;
       protected Locale locale;
       protected boolean lenient = false;
   
  @@ -458,6 +481,22 @@
           return functions;
       }
   
  +    public void setFactory(AbstractFactory factory){
  +        this.factory = factory;
  +    }
  +
  +    /**
  +     * Returns the AbstractFactory installed on this context.
  +     * If none has been installed, it calls getFactory() on
  +     * the parent context.
  +     */
  +    public AbstractFactory getFactory(){
  +        if (factory == null && parentContext != null){
  +            return parentContext.getFactory();
  +        }
  +        return factory;
  +    }
  +
       /**
        * Set the locale for this context.  The value of the "lang"
        * attribute as well as the the lang() function will be
  @@ -520,6 +559,22 @@
        * </ul>
        */
       public abstract void setValue(String xpath, Object value);
  +
  +    /**
  +     * The same as setValue, except it creates intermediate elements of
  +     * the path by invoking an AbstractFactory, which should first be
  +     * installed on the context by calling "setFactory".
  +     * <p>
  +     * Will throw an exception if one of the following conditions occurs:
  +     * <ul>
  +     * <li>Elements of the xpath aleady exist, by the path does not in
  +     *  fact describe an existing property
  +     * <li>The AbstractFactory fails to create an instance for an intermediate
  +     * element.
  +     * <li>The property is not writable (no public, non-static set method)
  +     * </ul>
  +     */
  +    public abstract void createPath(String xpath, Object value);
   
       /**
        * Traverses the xpath and returns a List of objects. Even if
  
  
  
  1.2       +19 -5     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/PackageFunctions.java
  
  Index: PackageFunctions.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/PackageFunctions.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- PackageFunctions.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ PackageFunctions.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/PackageFunctions.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/PackageFunctions.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -88,14 +88,22 @@
    *  a member of the package described by this PackageFunctions object.</dd>
    * </dl>
    *
  + * <p>
  + * If the first argument of a method or constructor is ExpressionContext, the
  + * expression context in which the function is evaluated is passed to
  + * the method.
  + * </p>
  + * <p>
    * There is one PackageFunctions object registered by default with each
    * JXPathContext.  It does not have a namespace and uses no class prefix.
    * The existence of this object allows us to use XPaths like:
    * <code>"java.util.Date.new()"</code> and <code>"length('foo')"</code>
    * without the explicit registration of any extension functions.
  + * </p>
  +
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class PackageFunctions implements Functions {
       private String classPrefix;
  @@ -129,11 +137,17 @@
               Object target = parameters[0];
               if (target != null){
                   if (target instanceof ExpressionContext){
  -                    target = ((ExpressionContext)target).getContextNodePointer().getValue();
  +                    Pointer pointer = ((ExpressionContext)target).getContextNodePointer();
  +                    if (pointer != null){
  +                        target = pointer.getValue();
  +                    }
  +                    else {
  +                        target = null;
  +                    }
                   }
               }
               if (target != null){
  -                Method method = Types.lookupMethod(Object.class, name, parameters);
  +                Method method = Types.lookupMethod(target.getClass(), name, parameters);
                   if (method != null){
                       return new MethodFunction(method);
                   }
  
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/AbstractFactory.java
  
  Index: AbstractFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/AbstractFactory.java,v 1.1 2002/04/10 03:40:19 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:19 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath;
  
  import java.util.*;
  
  /**
   * The {@link JXPathContext#createPath JXPathContext.createPath()} method of JXPathContext can create
   * missing objects as it traverses an XPath; it utilizes an AbstractFactory for that purpose.
   * Install a factory on JXPathContext by calling {@link JXPathContext#setFactory
   * JXPathContext.setFactory()}.
   * <p>
   * All methods of this class return false.  Override any of them to
   * return true to indicate that the factory has successfully created the described object.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:19 $
   */
  public abstract class AbstractFactory {
  
      /**
       * The parameters may describe a collection element or an individual object. It is up
       * to the factory to infer which one it is. If it is a collection, the
       * factory should check if the collection exists.  If not, it should create
       * the collection. Then it should create the index'th element of the collection
       * and return it.
       * <p>
       * If the parameters describe an individual object, the factory should only
       * create an object if index == 0.
       * <p>
       * Return the created object or <b>null</b> if this factory cannot create
       * the requested object.
       */
      public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index){
          return false;
      }
  
      /**
       * The factory should expand the collection to the specified size and return true. If
       * it cannot expand the collection, it should return false.
       */
      public boolean expandCollection(JXPathContext context, Pointer pointer, Object parent, String name, int size){
          return false;
      }
  
      /**
       * Create a new object and set it on the specified variable
       */
      public boolean declareVariable(JXPathContext context, String name){
          return false;
      }
  }
  
  
  1.2       +17 -11    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/ConstructorFunction.java
  
  Index: ConstructorFunction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/ConstructorFunction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ConstructorFunction.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ ConstructorFunction.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/ConstructorFunction.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/ConstructorFunction.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -69,11 +69,12 @@
    * An extension function that creates an instance using a constructor.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class ConstructorFunction implements Function {
   
       private Constructor constructor;
  +    private static final Object EMPTY_ARRAY[] = new Object[0];
   
       public ConstructorFunction(Constructor constructor){
           this.constructor = constructor;
  @@ -82,18 +83,23 @@
       /**
        * Converts parameters to suitable types and invokes the constructor.
        */
  -    public Object invoke(Object[] parameters){
  +    public Object invoke(ExpressionContext context, Object[] parameters){
           try {
               Object[] args;
               if (parameters == null){
  -                args = null;
  +                parameters = EMPTY_ARRAY;
               }
  -            else {
  -                Class types[] = constructor.getParameterTypes();
  -                args = new Object[parameters.length];
  -                for (int i = 0; i < args.length; i++){
  -                    args[i] = Types.convert(parameters[i], types[i]);
  -                }
  +            int pi = 0;
  +            Class types[] = constructor.getParameterTypes();
  +            if (types.length > 0 && ExpressionContext.class.isAssignableFrom(types[0])){
  +                pi = 1;
  +            }
  +            args = new Object[parameters.length + pi];
  +            if (pi == 1){
  +                args[0] = context;
  +            }
  +            for (int i = 0; i < parameters.length; i++){
  +                args[i + pi] = Types.convert(parameters[i], types[i]);
               }
               return constructor.newInstance(args);
           }
  
  
  
  1.2       +29 -17    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/MethodFunction.java
  
  Index: MethodFunction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/MethodFunction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MethodFunction.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ MethodFunction.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/MethodFunction.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/MethodFunction.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -67,47 +67,59 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class MethodFunction implements Function {
   
       private Method method;
  +    private static final Object EMPTY_ARRAY[] = new Object[0];
   
       public MethodFunction(Method method){
           this.method = method;
       }
   
  -    /**
  -     */
  -    public Object invoke(Object[] parameters){
  +    public Object invoke(ExpressionContext context, Object[] parameters){
           try {
               Object target;
               Object[] args;
               if (Modifier.isStatic(method.getModifiers())){
                   target = null;
                   if (parameters == null){
  -                    args = null;
  +                    parameters = EMPTY_ARRAY;
                   }
  -                else {
  -                    Class types[] = method.getParameterTypes();
  -                    args = new Object[parameters.length];
  -                    for (int i = 0; i < args.length; i++){
  -                        args[i] = Types.convert(parameters[i], types[i]);
  -                    }
  +                int pi = 0;
  +                Class types[] = method.getParameterTypes();
  +                if (types.length >= 1 && ExpressionContext.class.isAssignableFrom(types[0])){
  +                    pi = 1;
  +                }
  +                args = new Object[parameters.length + pi];
  +                if (pi == 1){
  +                    args[0] = context;
  +                }
  +                for (int i = 0; i < parameters.length; i++){
  +                    args[i + pi] = Types.convert(parameters[i], types[i + pi]);
                   }
               }
               else {
  -                target = Types.convert(parameters[0], method.getDeclaringClass());
  +                int pi = 0;
                   Class types[] = method.getParameterTypes();
  -                args = new Object[parameters.length - 1];
  -                for (int i = 0; i < args.length; i++){
  -                    args[i] = Types.convert(parameters[i + 1], types[i]);
  +                if (types.length >= 1 && ExpressionContext.class.isAssignableFrom(types[0])){
  +                    pi = 1;
  +                }
  +                target = Types.convert(parameters[0], method.getDeclaringClass());
  +                args = new Object[parameters.length - 1 + pi];
  +                if (pi == 1){
  +                    args[0] = context;
  +                }
  +                for (int i = 1; i < parameters.length; i++){
  +                    args[pi + i - 1] = Types.convert(parameters[i], types[i - 1]);
                   }
               }
   
               return method.invoke(target, args);
           }
           catch (Exception exception){
  +            exception.printStackTrace();
               // TBD
               throw new RuntimeException("Cannot invoke " + method + ": " + exception);
           }
  
  
  
  1.2       +61 -10    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/Types.java
  
  Index: Types.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/Types.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Types.java	23 Aug 2001 00:46:58 -0000	1.1
  +++ Types.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/Types.java,v 1.1 2001/08/23 00:46:58 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:58 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/functions/Types.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -67,7 +67,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:58 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class Types {
   
  @@ -251,14 +251,19 @@
               throw new RuntimeException("Ambigous method call: " + name);
           }
           return method;
  -   }
  +    }
  +
       public static int matchParameterTypes(Class types[], Object parameters[]){
  -        if (types.length != parameters.length){
  +        int pi = 0;
  +        if (types.length >= 1 && ExpressionContext.class.isAssignableFrom(types[0])){
  +            pi++;
  +        }
  +        if (types.length != parameters.length + pi){
               return NO_MATCH;
           }
           int totalMatch = EXACT_MATCH;
  -        for (int i = 0; i < types.length; i++){
  -            int match = matchType(types[i], parameters[i]);
  +        for (int i = 0; i < parameters.length; i++){
  +            int match = matchType(types[i + pi], parameters[i]);
               if (match == NO_MATCH){
                   return NO_MATCH;
               }
  @@ -291,6 +296,14 @@
       }
   
       public static boolean canConvert(Object object, Class toType){
  +        if (object == null){
  +            return true;
  +        }
  +
  +        if (toType == Object.class){
  +            return true;
  +        }
  +
           Class fromType = object.getClass();
           if (fromType.equals(toType)){
               return true;
  @@ -330,9 +343,30 @@
               if (Collection.class.isAssignableFrom(toType)){
                   return true;
               }
  -            // TBD: array arguments
  -            Object value = ((ExpressionContext)object).getContextNodePointer().getValue();
  -            return canConvert(value, toType);
  +            Pointer pointer = ((ExpressionContext)object).getContextNodePointer();
  +            if (pointer != null){
  +                Object value = pointer.getValue();
  +                return canConvert(value, toType);
  +            }
  +        }
  +        else if (fromType.isArray()){
  +            if (Array.getLength(object) == 1){
  +                Object value = Array.get(object, 0);
  +                return canConvert(value, toType);
  +            }
  +        }
  +        else if (object instanceof List){
  +            if (((List)object).size() == 1){
  +                Object value = ((List)object).get(0);
  +                return canConvert(value, toType);
  +            }
  +        }
  +        else if (object instanceof Collection){
  +            if (!((Collection)object).isEmpty()){
  +                Iterator it = ((Collection)object).iterator();
  +                Object value = it.next();
  +                return canConvert(value, toType);
  +            }
           }
   
           // TBD: date conversion to/from string
  @@ -344,6 +378,10 @@
               return null;
           }
   
  +        if (toType == Object.class){
  +            return object;
  +        }
  +
           if (object instanceof ExpressionContext){
               if (Collection.class.isAssignableFrom(toType)){
                   List list = ((ExpressionContext)object).getContextNodeList();
  @@ -426,6 +464,19 @@
               if (toType == double.class || toType == Double.class){
                   return new Double((String)object);
               }
  +        }
  +        else if (fromType.isArray()){
  +            Object value = Array.get(object, 0);
  +            return convert(value, toType);
  +        }
  +        else if (object instanceof List){
  +            Object value = ((List)object).get(0);
  +            return convert(value, toType);
  +        }
  +        else if (object instanceof Collection){
  +            Iterator it = ((Collection)object).iterator();
  +            Object value = it.next();
  +            return convert(value, toType);
           }
           return object;
       }
  
  
  
  1.6       +198 -73   jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/EvalContext.java
  
  Index: EvalContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/EvalContext.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- EvalContext.java	26 Sep 2001 23:37:38 -0000	1.5
  +++ EvalContext.java	10 Apr 2002 03:40:19 -0000	1.6
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/EvalContext.java,v 1.5 2001/09/26 23:37:38 dmitri Exp $
  - * $Revision: 1.5 $
  - * $Date: 2001/09/26 23:37:38 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/EvalContext.java,v 1.6 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.6 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -61,6 +61,7 @@
    */
   package org.apache.commons.jxpath.ri;
   
  +import org.apache.commons.jxpath.JXPathContext;
   import org.apache.commons.jxpath.ri.Compiler;
   import org.apache.commons.jxpath.ri.compiler.*;
   import org.apache.commons.jxpath.ri.pointers.*;
  @@ -79,18 +80,72 @@
    * implement behavior of various XPath axes: "child::", "parent::" etc.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.5 $ $Date: 2001/09/26 23:37:38 $
  + * @version $Revision: 1.6 $ $Date: 2002/04/10 03:40:19 $
    */
  -public abstract class EvalContext implements ExpressionContext {
  +public abstract class EvalContext {
       protected EvalContext parentContext;
       protected RootContext rootContext;
       protected int position = 0;
       private boolean startedSetIteration = false;
  +    private EvalExpressionContext expressionContext;
   
       public EvalContext(EvalContext parentContext){
           this.parentContext = parentContext;
       }
   
  +    public class EvalExpressionContext implements ExpressionContext {
  +        public Pointer getContextNodePointer(){
  +            return getCurrentNodePointer();
  +        }
  +
  +        public JXPathContext getJXPathContext(){
  +            return getRootContext().getJXPathContext();
  +        }
  +
  +        public int getPosition(){
  +            return position;
  +        }
  +
  +        public List getContextNodeList(){
  +            int pos = position;
  +            if (pos != 0){
  +                reset();
  +            }
  +            List list = new ArrayList();
  +            while(next()){
  +                list.add(getCurrentNodePointer());
  +            }
  +            if (pos != 0){
  +                setPosition(pos);
  +            }
  +            else {
  +                reset();
  +            }
  +            return list;
  +        }
  +
  +        public String toString(){
  +            Pointer ptr = getContextNodePointer();
  +            if (ptr == null){
  +                return "Empty expression context";
  +            }
  +            else {
  +                return "Expression context [" + getPosition() + "] " + ptr.asPath();
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Produces an ExpressionContext that when it needs to pass one to
  +     * an extenstion function.
  +     */
  +    public ExpressionContext getExpressionContext(){
  +        if (expressionContext == null){
  +            expressionContext = new EvalExpressionContext();
  +        }
  +        return expressionContext;
  +    }
  +
       /**
        * Returns the root context of the path, which provides easy
        * access to variables and functions.
  @@ -104,9 +159,8 @@
   
       /**
        * Sets current position = 0, which is the pre-iteration state.
  -     * @deprecated
        */
  -    protected void reset(){
  +    public void reset(){
           position = 0;
       }
   
  @@ -120,17 +174,13 @@
        * step's criteria.  Otherwise, returns the current pointer.
        */
       public Pointer getContextNodePointer(){
  -        if (position == 0){
  -            while(nextSet()){
  -                if (next()){
  -                    return getCurrentNodePointer();
  -                }
  +        reset();
  +        while(nextSet()){
  +            if (next()){
  +                return getCurrentNodePointer();
               }
  -            return null;
  -        }
  -        else {
  -            return getCurrentNodePointer();
           }
  +        return null;
       }
   
       /**
  @@ -158,7 +208,7 @@
        * Resets the current position and node.
        */
       public boolean nextSet(){
  -        setPosition(0);     // Restart iteration within the set
  +        reset();     // Restart iteration within the set
   
           // Most of the time you have one set per parent node
           // First time this method is called, we should look for
  @@ -329,9 +379,7 @@
               return path((LocationPath)expression, firstMatch);
   
           case Expression.OP_EXPRESSION_PATH:
  -            return expressionPath(((ExpressionPath)expression).getExpression(),
  -                                  ((ExpressionPath)expression).getPredicates(),
  -                                  ((ExpressionPath)expression).getSteps(), firstMatch);
  +            return expressionPath((ExpressionPath)expression, firstMatch);
   
           case Expression.OP_FUNCTION:
               return function(((ExtensionFunction)expression).getFunctionName(),
  @@ -731,17 +779,7 @@
           else {
               rootContext = this;
           }
  -        if (firstMatch){
  -            boolean basic = path.getEvaluationHint(LocationPath.BASIC_PATH_HINT).equals(Boolean.TRUE);
  -            if (basic){
  -                Object result = tryBasicPath(new InitialContext(rootContext), steps);
  -                if (result != FAILURE){
  -                    return result;
  -                }
  -            }
  -        }
  -        EvalContext aContext = new InitialContext(rootContext);
  -        return evalSteps(aContext, steps, firstMatch);
  +        return evalSteps(new InitialContext(rootContext), path, firstMatch);
       }
   
   
  @@ -754,62 +792,111 @@
        * and objects with Dynamic Properties, but does not work with
        * DOM objects.
        */
  -    private Object tryBasicPath(EvalContext context, Step steps[]){
  -        NodePointer ptr = (NodePointer)context.getContextNodePointer();
  -        if (ptr == null || !(ptr instanceof PropertyOwnerPointer)){
  +    private Object tryBasicPath(NodePointer parentPointer, Step steps[]){
  +        if (parentPointer == null){
               return FAILURE;
           }
  -        PropertyOwnerPointer pointer = (PropertyOwnerPointer)ptr.clone();
  +
  +        NodePointer pointer = (NodePointer)parentPointer.clone();
           for (int i = 0; i < steps.length; i++){
  -            String propertyName = ((NodeNameTest)steps[i].getNodeTest()).getNodeName().getName();
  -            pointer = pointer.getPropertyPointer();
  -            ((PropertyPointer)pointer).setPropertyName(propertyName);
  +            pointer = getPropertyPointer(pointer, ((NodeNameTest)steps[i].getNodeTest()).getNodeName().getName());
  +            if (pointer == null){
  +                return FAILURE;
  +            }
   
               Expression predicates[] = steps[i].getPredicates();
               if (predicates != null && predicates.length != 0){
  -                boolean dynamicProperty = false;
  -                Expression expr = (Expression)predicates[0].getEvaluationHint(CoreOperation.DYNAMIC_PROPERTY_ACCESS_HINT);
  -                if (expr != null){
  -                    String prop = stringValue(eval(expr, true));
  -                    pointer = pointer.getPropertyPointer();
  -                    ((PropertyPointer)pointer).setPropertyName(prop);
  +                pointer = processBasicPredicates(pointer, predicates);
  +            }
  +            else {
  +                // If we are in the middle of a path, we interpret
  +                // a component like "foo" as "foo[1]"
  +                if (i < steps.length - 1){
  +                    pointer.setIndex(0);
                   }
  -                else {
  -                    Object predicate = eval(predicates[0], true);
  -                    if (predicate instanceof EvalContext){
  -                        predicate = ((EvalContext)predicate).getContextNodePointer();
  -                    }
  -                    if (predicate instanceof Pointer){
  -                        predicate = ((Pointer)predicate).getValue();
  -                    }
  -                    if (predicate == null){
  -                        throw new RuntimeException("Predicate is null: " + predicates[0]);
  -                    }
  -                    if (predicate instanceof Number){
  -                        int index = (int)(doubleValue(predicate) + 0.5);
  -                        if (index > 0 && index <= pointer.getLength()){
  -                            pointer.setIndex(index - 1);
  -                        }
  -                        else {
  -                            return null;
  -                        }
  +            }
  +        }
  +//        System.err.println("RETURNING: " + pointer);
  +        return pointer;
  +    }
  +
  +    private NodePointer processBasicPredicates(NodePointer pointer, Expression[] predicates){
  +        if (predicates == null || predicates.length == 0){
  +            return pointer;
  +        }
  +
  +        for (int i = 0; pointer != null && i < predicates.length; i++){
  +            Expression expr = (Expression)predicates[i].getEvaluationHint(CoreOperation.DYNAMIC_PROPERTY_ACCESS_HINT);
  +            if (expr != null){
  +                String prop = stringValue(eval(expr, true));
  +                pointer = getPropertyPointer(pointer, prop);
  +                if (pointer instanceof NullPropertyPointer){
  +                    ((NullPropertyPointer)pointer).setDynamic(true);
  +                }
  +            }
  +            else {
  +                Object predicate = eval(predicates[i], true);
  +                if (predicate instanceof EvalContext){
  +                    predicate = ((EvalContext)predicate).getContextNodePointer();
  +                }
  +                if (predicate instanceof Pointer){
  +                    predicate = ((Pointer)predicate).getValue();
  +                }
  +                if (predicate == null){
  +                    throw new RuntimeException("Predicate is null: " + predicates[i]);
  +                }
  +                if (predicate instanceof Number){
  +                    int index = (int)(doubleValue(predicate) + 0.5);
  +                    if (index > 0 && index <= pointer.getLength()){
  +                        pointer.setIndex(index - 1);
                       }
  -                    else if (!booleanValue(predicate)){
  -                        return null;
  +                    else {
  +                        pointer = new NullElementPointer(pointer, index - 1);
                       }
                   }
  +                else if (!booleanValue(predicate)){
  +                    pointer = null;
  +                }
  +            }
  +        }
  +        return pointer;
  +    }
  +
  +    private NodePointer getPropertyPointer(NodePointer ptr, String property){
  +        NodePointer pointer = ptr;
  +        while (true){
  +            if (pointer instanceof VariablePointer){
  +                pointer = ((VariablePointer)pointer).getValuePointer();
  +            }
  +            else if (pointer instanceof ContainerPointer){
  +                pointer = ((ContainerPointer)pointer).getValuePointer();
               }
               else {
  -                return FAILURE;
  +                break;
               }
           }
  -        return pointer;
  +
  +        if (pointer != null && !(pointer instanceof PropertyOwnerPointer)){
  +            return null;
  +        }
  +
  +        PropertyPointer prop;
  +        if (pointer != null){
  +            prop = ((PropertyOwnerPointer)pointer).getPropertyPointer();
  +        }
  +        else {
  +            prop = new NullPropertyPointer(ptr);
  +        }
  +
  +        prop.setPropertyName(property);
  +        return prop.childNodePointer();
       }
   
       /**
        * Walks an expression path (a path that starts with an expression)
        */
  -    protected Object expressionPath(Expression expression, Expression predicates[], Step steps[], boolean firstMatch){
  +    protected Object expressionPath(ExpressionPath path, boolean firstMatch){
  +        Expression expression = path.getExpression();
           Object value = eval(expression, false);
           EvalContext context;
           if (value instanceof InitialContext){
  @@ -825,18 +912,52 @@
           else {
               context = getRootContext().getConstantContext(value);
           }
  +
  +        Expression predicates[] = path.getPredicates();
  +        if (firstMatch){
  +            if (path.getEvaluationHint(ExpressionPath.BASIC_PREDICATES_HINT).equals(Boolean.TRUE)){
  +                EvalContext ctx = new InitialContext(context);
  +                NodePointer ptr = (NodePointer)ctx.getContextNodePointer();
  +                if (ptr != null &&
  +                        (ptr.getIndex() == NodePointer.WHOLE_COLLECTION ||
  +                         predicates == null || predicates.length == 0)){
  +                    NodePointer pointer = processBasicPredicates(ptr, predicates);
  +                    if (pointer != null){
  +                        Object result = tryBasicPath(pointer, path.getSteps());
  +                        if (result != FAILURE){
  +                            return result;
  +                        }
  +                    }
  +                }
  +            }
  +        }
  +
           if (predicates != null){
               for (int j = 0; j < predicates.length; j++){
  +//                System.err.println("PREDICATE: " + predicates[j]);
                   context = new PredicateContext(context, predicates[j]);
               }
           }
  -        return evalSteps(context, steps, firstMatch);
  +        return evalSteps(context, path, firstMatch);
       }
   
       /**
        * Given a root context, walks a path therefrom
        */
  -    private Object evalSteps(EvalContext context, Step steps[], boolean firstMatch){
  +    private Object evalSteps(EvalContext context, Path path, boolean firstMatch){
  +        Step steps[] = path.getSteps();
  +        if (firstMatch && steps.length != 0){
  +            boolean basic = path.getEvaluationHint(Path.BASIC_PATH_HINT).equals(Boolean.TRUE);
  +            if (basic){
  +                EvalContext ctx = new InitialContext(context);
  +                NodePointer ptr = (NodePointer)ctx.getContextNodePointer();
  +                Object result = tryBasicPath(ptr, steps);
  +                if (result != FAILURE){
  +                    return result;
  +                }
  +            }
  +        }
  +
           for (int i = 0; i < steps.length; i++){
               context = createContextForStep(context, steps[i].getAxis(), steps[i].getNodeTest());
               Expression predicates[] = steps[i].getPredicates();
  @@ -901,7 +1022,11 @@
           if (arguments != null){
               parameters = new Object[arguments.length];
               for (int i = 0; i < arguments.length; i++){
  -                parameters[i] = eval(arguments[i]);
  +                Object param = eval(arguments[i], false);
  +                if (param instanceof EvalContext){
  +                    param = ((EvalContext)param).getExpressionContext();
  +                }
  +                parameters[i] = param;
               }
           }
           Function function = getRootContext().getFunction(functionName, parameters);
  @@ -910,7 +1035,7 @@
                    Arrays.asList(parameters));
           }
   
  -        return function.invoke(parameters);
  +        return function.invoke(getExpressionContext(), parameters);
       }
   
       /**
  @@ -962,7 +1087,7 @@
           // Move the position to the beginning and iterate through
           // the context to count nodes.
           int old = getCurrentPosition();
  -        setPosition(0);
  +        reset();
           int count = 0;
           while(next()){
               count++;
  
  
  
  1.7       +90 -14    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java
  
  Index: JXPathContextReferenceImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- JXPathContextReferenceImpl.java	26 Sep 2001 23:37:38 -0000	1.6
  +++ JXPathContextReferenceImpl.java	10 Apr 2002 03:40:19 -0000	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java,v 1.6 2001/09/26 23:37:38 dmitri Exp $
  - * $Revision: 1.6 $
  - * $Date: 2001/09/26 23:37:38 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java,v 1.7 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.7 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -81,7 +81,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.6 $ $Date: 2001/09/26 23:37:38 $
  + * @version $Revision: 1.7 $ $Date: 2002/04/10 03:40:19 $
    */
   public class JXPathContextReferenceImpl extends JXPathContext
   {
  @@ -90,12 +90,53 @@
       private static final PackageFunctions genericFunctions = new PackageFunctions("", null);
       private static boolean useSoftCache = true;
       private static int cleanupCount = 0;
  +    private static Vector nodeFactories = new Vector();
  +    private static NodePointerFactory nodeFactoryArray[] = null;
  +    static {
  +        nodeFactories.add(new BeanPointerFactory());
  +        nodeFactories.add(new DynamicPointerFactory());
  +        nodeFactories.add(new DOMPointerFactory());
  +        nodeFactories.add(new ContainerPointerFactory());
  +        createNodeFactoryArray();
  +    }
   
       // The frequency of the cache cleanup
       private static final int CLEANUP_THRESHOLD = 500;
   
       protected JXPathContextReferenceImpl(JXPathContext parentContext, Object contextBean){
           super(parentContext, contextBean);
  +        synchronized (nodeFactories){
  +            createNodeFactoryArray();
  +        }
  +    }
  +
  +    private static void createNodeFactoryArray(){
  +        if (nodeFactoryArray == null){
  +            nodeFactoryArray = (NodePointerFactory[])nodeFactories.toArray(new NodePointerFactory[0]);
  +            Arrays.sort(nodeFactoryArray, new Comparator(){
  +                public int compare(Object a, Object b){
  +                    int orderA = ((NodePointerFactory)a).getOrder();
  +                    int orderB = ((NodePointerFactory)b).getOrder();
  +                    return orderA - orderB;
  +                }
  +            });
  +        }
  +    }
  +
  +    /**
  +     * Call this with a custom NodePointerFactory to add support for
  +     * additional types of objects.  Make sure the factory returns
  +     * a name that puts it in the right position on the list of factories.
  +     */
  +    public static void addNodePointerFactory(NodePointerFactory factory){
  +        synchronized (nodeFactories){
  +            nodeFactories.add(factory);
  +            nodeFactoryArray = null;
  +        }
  +    }
  +
  +    public static NodePointerFactory[] getNodePointerFactories(){
  +        return nodeFactoryArray;
       }
   
       private static Expression compile(String xpath){
  @@ -144,6 +185,7 @@
        * types are wrapped into objects.
        */
       public Object getValue(String xpath){
  +//        System.err.println("XPATH: " + xpath);
           Object result = eval(xpath, true);
           if (result == null && !lenient){
               throw new RuntimeException("No value for xpath: " + xpath);
  @@ -187,6 +229,7 @@
        * in the graph, the List will be empty.
        */
       public List eval(String xpath){
  +//        System.err.println("XPATH: " + xpath);
           Object result = eval(xpath, false);
           List list = new ArrayList();
           if (result instanceof EvalContext){
  @@ -208,6 +251,7 @@
       }
   
       public Pointer locateValue(String xpath){
  +//        System.err.println("XPATH: " + xpath);
           Object result = eval(xpath, true);
           if (result instanceof EvalContext){
               result = ((EvalContext)result).getContextNodePointer();
  @@ -223,23 +267,56 @@
       /**
        */
       public void setValue(String xpath, Object value){
  +        try {
  +            setValue(xpath, value, false);
  +        }
  +        catch (Throwable ex){
  +            throw new RuntimeException("Exception trying to set value with xpath " +
  +                    xpath + ". " + ex.getMessage());
  +        }
  +    }
  +
  +    /**
  +     */
  +    public void createPath(String xpath, Object value){
  +//        System.err.println("CREATING XPATH: " + xpath);
  +        try {
  +            setValue(xpath, value, true);
  +        }
  +        catch (Throwable ex){
  +            throw new RuntimeException("Exception trying to create xpath " +
  +                    xpath + ". " + ex.getMessage());
  +        }
  +    }
  +
  +    private void setValue(String xpath, Object value, boolean create){
           Object result = eval(xpath, true);
  +//        System.err.println("RESULT: " + result);
  +        Pointer pointer = null;
  +
           if (result instanceof Pointer){
  -            ((Pointer)result).setValue(value);
  +            pointer = (Pointer)result;
           }
           else if (result instanceof EvalContext){
               EvalContext ctx = (EvalContext)result;
  -            Pointer ptr = ctx.getContextNodePointer();
  -            if (ptr != null){
  -                ptr.setValue(value);
  -            }
  -            else {
  -                throw new RuntimeException("Cannot set value for xpath: " + xpath + ": no such property");
  -            }
  +            pointer = ctx.getContextNodePointer();
           }
           else {
               throw new RuntimeException("Cannot set value for xpath: " + xpath);
           }
  +//        Pointer p = pointer;
  +//        while (p != null){
  +//            System.err.println("PTR: " + p.getClass() + " " + p.asPath());
  +//            if (p instanceof NodePointer){
  +//                p = ((NodePointer)p).getParent();
  +//            }
  +//        }
  +        if (create){
  +            ((NodePointer)pointer).createPath(this, value);
  +        }
  +        else {
  +            pointer.setValue(value);
  +        }
       }
   
       public List locate(String xpath){
  @@ -267,7 +344,6 @@
           Expression expr = compile(xpath);
           NodePointer pointer = NodePointer.createNodePointer(new QName(null, "root"), getContextBean(), getLocale());
           EvalContext ctx = new RootContext(this, pointer);
  -//        System.err.println("XPATH = " + xpath);
           return ctx.eval(expr, firstMatchLookup);
       }
   
  @@ -299,7 +375,7 @@
               return new VariablePointer(vars, name);
           }
           else {
  -            throw new RuntimeException("Undefined variable: " + varName);
  +            return new VariablePointer(name);
           }
       }
   
  
  
  
  1.2       +7 -6      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/Parser.java
  
  Index: Parser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/Parser.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Parser.java	23 Aug 2001 00:46:59 -0000	1.1
  +++ Parser.java	10 Apr 2002 03:40:19 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/Parser.java,v 1.1 2001/08/23 00:46:59 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:59 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/Parser.java,v 1.2 2002/04/10 03:40:19 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:19 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -69,7 +69,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:59 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:19 $
    */
   public class Parser {
   
  @@ -88,8 +88,9 @@
               expr = parser.parseExpression();
           }
           catch (ParseException e) {
  -            System.err.println("Failed: " + expression );
  -            e.printStackTrace();
  +//            System.err.println("Failed: " + expression );
  +//            e.printStackTrace();
  +            throw new RuntimeException(e.getMessage());
           }
           return expr;
       }
  
  
  
  1.4       +11 -6     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AncestorContext.java
  
  Index: AncestorContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AncestorContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AncestorContext.java	21 Sep 2001 23:22:43 -0000	1.3
  +++ AncestorContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AncestorContext.java,v 1.3 2001/09/21 23:22:43 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:43 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AncestorContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,7 +73,7 @@
    * EvalContext that walks the "ancestor::" and "ancestor-or-self::" axes.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:43 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class AncestorContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -97,9 +97,14 @@
           return currentNodePointer;
       }
   
  +    public void reset(){
  +        super.reset();
  +        setStarted = false;
  +    }
  +
       public boolean setPosition(int position){
  -        if (position == 0 || position < getCurrentPosition()){
  -            setStarted = false;
  +        if (position < getCurrentPosition()){
  +            reset();
           }
   
           while (getCurrentPosition() < position){
  
  
  
  1.3       +12 -8     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AttributeContext.java
  
  Index: AttributeContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AttributeContext.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AttributeContext.java	21 Sep 2001 23:22:43 -0000	1.2
  +++ AttributeContext.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AttributeContext.java,v 1.2 2001/09/21 23:22:43 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:43 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/AttributeContext.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -71,7 +71,7 @@
    * EvalContext that walks the "attribute::" axis.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:43 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class AttributeContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -92,11 +92,15 @@
           return currentNodePointer;
       }
   
  +    public void reset(){
  +        setStarted = false;
  +        iterator = null;
  +        super.reset();
  +    }
  +
       public boolean setPosition(int position){
  -        if (position == 0 || position < getCurrentPosition()){
  -            setStarted = false;
  -            iterator = null;
  -            super.setPosition(0);
  +        if (position < getCurrentPosition()){
  +            reset();
           }
   
           while (getCurrentPosition() < position){
  
  
  
  1.4       +22 -15    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ChildContext.java
  
  Index: ChildContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ChildContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ChildContext.java	21 Sep 2001 23:22:43 -0000	1.3
  +++ ChildContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ChildContext.java,v 1.3 2001/09/21 23:22:43 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:43 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ChildContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -76,7 +76,7 @@
    * "preceding-sibling::" axes.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:43 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class ChildContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -92,6 +92,11 @@
       }
   
       public NodePointer getCurrentNodePointer(){
  +        if (position == 0){
  +            if (!setPosition(1)){
  +                return null;
  +            }
  +        }
           if (iterator != null){
               return iterator.getNodePointer();
           }
  @@ -127,22 +132,21 @@
           return setPosition(getCurrentPosition() + 1);
       }
   
  +    public void reset(){
  +        super.reset();
  +        iterator = null;
  +    }
  +
       public boolean setPosition(int position){
           int oldPosition = getCurrentPosition();
           super.setPosition(position);
  -        if (position == 0){
  -            iterator = null;
  -            return true;
  +        if (oldPosition == 0){
  +            prepare();
           }
  -        else {
  -            if (oldPosition == 0){
  -                prepare();
  -            }
  -            if (iterator == null){
  -                return false;
  -            }
  -            return iterator.setPosition(position);
  +        if (iterator == null){
  +            return false;
           }
  +        return iterator.setPosition(position);
       }
   
       /**
  @@ -150,6 +154,9 @@
        */
       private void prepare(){
           NodePointer parent = parentContext.getCurrentNodePointer();
  +        if (parent == null){
  +            return;
  +        }
           if (startFromParentLocation){
               iterator = parent.siblingIterator(nodeTest, reverse);
           }
  
  
  
  1.4       +27 -14    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/DescendantContext.java
  
  Index: DescendantContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/DescendantContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DescendantContext.java	21 Sep 2001 23:22:43 -0000	1.3
  +++ DescendantContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/DescendantContext.java,v 1.3 2001/09/21 23:22:43 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:43 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/DescendantContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,7 +73,7 @@
    * axes.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:43 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class DescendantContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -89,13 +89,23 @@
       }
   
       public NodePointer getCurrentNodePointer(){
  +        if (position == 0){
  +            if (!setPosition(1)){
  +                return null;
  +            }
  +        }
           return currentNodePointer;
       }
   
  +    public void reset(){
  +        super.reset();
  +        setStarted = false;
  +    }
  +
       public boolean setPosition(int position){
  -        if (position == 0 || position < this.position){
  -            stack = new Stack();
  -            setStarted = false;
  +//        System.err.println("POSITION: " + position + " this.position=" + this.position);
  +        if (position < this.position){
  +            reset();
           }
   
           while (this.position < position){
  @@ -109,14 +119,17 @@
       public boolean next(){
           if (!setStarted){
               setStarted = true;
  +            stack = new Stack();
               currentNodePointer = parentContext.getCurrentNodePointer();
  -            if (!currentNodePointer.isLeaf()){
  -                stack.push(currentNodePointer.childIterator(null, false));
  -            }
  -            if (includeSelf){
  -                if (currentNodePointer.testNode(nodeTest)){
  -                    position++;
  -                    return true;
  +            if (currentNodePointer != null){
  +                if (!currentNodePointer.isLeaf()){
  +                    stack.push(currentNodePointer.childIterator(null, false));
  +                }
  +                if (includeSelf){
  +                    if (currentNodePointer.testNode(nodeTest)){
  +                        position++;
  +                        return true;
  +                    }
                   }
               }
           }
  
  
  
  1.3       +9 -6      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/InitialContext.java
  
  Index: InitialContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/InitialContext.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- InitialContext.java	21 Sep 2001 23:22:43 -0000	1.2
  +++ InitialContext.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/InitialContext.java,v 1.2 2001/09/21 23:22:43 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:43 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/InitialContext.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,7 +74,7 @@
    * on to the parent context.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:43 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class InitialContext extends EvalContext {
       private boolean startedSet = false;
  @@ -84,8 +84,11 @@
   
       public InitialContext(EvalContext parentContext){
           super(parentContext);
  -        nodePointer = (NodePointer)parentContext.getCurrentNodePointer().clone();
  -        collection = (nodePointer.getIndex() == NodePointer.WHOLE_COLLECTION);
  +        NodePointer ptr = parentContext.getCurrentNodePointer();
  +        if (ptr != null){
  +            nodePointer = (NodePointer)ptr.clone();
  +            collection = (nodePointer.getIndex() == NodePointer.WHOLE_COLLECTION);
  +        }
       }
   
       public Pointer getContextNodePointer(){
  
  
  
  1.2       +11 -7     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/NamespaceContext.java
  
  Index: NamespaceContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/NamespaceContext.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- NamespaceContext.java	21 Sep 2001 23:22:44 -0000	1.1
  +++ NamespaceContext.java	10 Apr 2002 03:40:20 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/NamespaceContext.java,v 1.1 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/NamespaceContext.java,v 1.2 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -71,7 +71,7 @@
    * EvalContext that walks the "namespace::" axis.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:20 $
    */
   public class NamespaceContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -92,11 +92,15 @@
           return currentNodePointer;
       }
   
  +    public void reset(){
  +        setStarted = false;
  +        iterator = null;
  +        super.reset();
  +    }
  +
       public boolean setPosition(int position){
  -        if (position == 0 || position < getCurrentPosition()){
  -            setStarted = false;
  -            iterator = null;
  -            super.setPosition(0);
  +        if (position < getCurrentPosition()){
  +            reset();
           }
   
           while (getCurrentPosition() < position){
  
  
  
  1.3       +9 -7      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ParentContext.java
  
  Index: ParentContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ParentContext.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ParentContext.java	21 Sep 2001 23:22:44 -0000	1.2
  +++ ParentContext.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ParentContext.java,v 1.2 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/ParentContext.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,7 +73,7 @@
    * EvalContext that walks the "parent::" axis.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class ParentContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -94,11 +94,13 @@
           return 1;
       }
   
  +    public void reset(){
  +        super.reset();
  +        setStarted = false;
  +    }
  +
       public boolean setPosition(int position){
           super.setPosition(position);
  -        if (position == 0){
  -            setStarted = false;
  -        }
           return position == 1;
       }
   
  
  
  
  1.4       +12 -7     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java
  
  Index: PrecedingOrFollowingContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PrecedingOrFollowingContext.java	21 Sep 2001 23:22:44 -0000	1.3
  +++ PrecedingOrFollowingContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java,v 1.3 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,7 +74,7 @@
    * EvalContext that walks the "preceding::" and "following::" axes.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class PrecedingOrFollowingContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -98,10 +98,15 @@
           return currentNodePointer;
       }
   
  +    public void reset(){
  +        super.reset();
  +        stack = new Stack();
  +        setStarted = false;
  +    }
  +
       public boolean setPosition(int position){
  -        if (position == 0 || position < this.position){
  -            stack = new Stack();
  -            setStarted = false;
  +        if (position < this.position){
  +            reset();
           }
   
           while (this.position < position){
  
  
  
  1.4       +15 -6     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PredicateContext.java
  
  Index: PredicateContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PredicateContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PredicateContext.java	21 Sep 2001 23:22:44 -0000	1.3
  +++ PredicateContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PredicateContext.java,v 1.3 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/PredicateContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,7 +73,7 @@
    * EvalContext that checks predicates.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class PredicateContext extends EvalContext {
       private Expression expression;
  @@ -140,6 +140,11 @@
       }
   
       public NodePointer getCurrentNodePointer(){
  +        if (position == 0){
  +            if (!setPosition(1)){
  +                return null;
  +            }
  +        }
           if (dynamicPropertyPointer != null){
               return dynamicPropertyPointer;
           }
  @@ -148,9 +153,13 @@
           }
       }
   
  -    public boolean nextSet(){
  -        position = 0;
  +    public void reset(){
  +        super.reset();
           done = false;
  +    }
  +
  +    public boolean nextSet(){
  +        reset();
           return parentContext.nextSet();
       }
   
  
  
  
  1.4       +9 -4      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/RootContext.java
  
  Index: RootContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/RootContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- RootContext.java	26 Sep 2001 01:21:54 -0000	1.3
  +++ RootContext.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/RootContext.java,v 1.3 2001/09/26 01:21:54 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/26 01:21:54 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/RootContext.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -61,6 +61,7 @@
    */
   package org.apache.commons.jxpath.ri.axes;
   
  +import org.apache.commons.jxpath.JXPathContext;
   import org.apache.commons.jxpath.ri.Compiler;
   import org.apache.commons.jxpath.ri.compiler.*;
   import org.apache.commons.jxpath.ri.pointers.*;
  @@ -73,7 +74,7 @@
    * EvalContext that is used to hold the root node for the path traversal.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/26 01:21:54 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class RootContext extends EvalContext {
       private boolean startedSet = false;
  @@ -89,6 +90,10 @@
           super(null);
           this.parent = parent;
           this.pointer = pointer;
  +    }
  +
  +    public JXPathContext getJXPathContext(){
  +        return parent;
       }
   
       public RootContext getRootContext(){
  
  
  
  1.3       +24 -18    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SelfContext.java
  
  Index: SelfContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SelfContext.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SelfContext.java	21 Sep 2001 23:22:44 -0000	1.2
  +++ SelfContext.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SelfContext.java,v 1.2 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SelfContext.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -61,6 +61,7 @@
    */
   package org.apache.commons.jxpath.ri.axes;
   
  +import org.apache.commons.jxpath.ExpressionContext;
   import org.apache.commons.jxpath.Pointer;
   import org.apache.commons.jxpath.ri.Compiler;
   import org.apache.commons.jxpath.ri.compiler.*;
  @@ -73,7 +74,7 @@
    * EvalContext that returns the current node from the parent context if the test succeeds.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class SelfContext extends EvalContext {
       private NodeTest nodeTest;
  @@ -86,14 +87,20 @@
           this.nodeTest = nodeTest;
       }
   
  +    public ExpressionContext getExpressionContext(){
  +        return parentContext.getExpressionContext();
  +    }
  +
       public Pointer getContextNodePointer(){
  -        if (setPosition(1)){
  -            return contextNodePointer;
  -        }
  -        return null;
  +        return parentContext.getContextNodePointer();
       }
   
       public NodePointer getCurrentNodePointer(){
  +        if (position == 0){
  +            if (!setPosition(1)){
  +                return null;
  +            }
  +        }
           return nodePointer;
       }
   
  @@ -101,16 +108,19 @@
           return setPosition(getCurrentPosition() + 1);
       }
   
  +    public void reset(){
  +        super.reset();
  +        startedSet = false;
  +    }
  +
       public boolean setPosition(int position){
  -        super.setPosition(position);
  -        if (position == 0){
  -            startedSet = false;
  -            return true;
  +        if (position != 1){
  +            return false;
           }
  -
  +        super.setPosition(position);
           if (!startedSet){
               startedSet = true;
  -            contextNodePointer = (NodePointer)parentContext.getContextNodePointer();
  +            contextNodePointer = (NodePointer)parentContext.getCurrentNodePointer();
           }
   
           if (contextNodePointer == null){
  @@ -118,10 +128,6 @@
           }
   
           nodePointer = (NodePointer)contextNodePointer.clone();
  -        if (position < 1 || position > nodePointer.getLength()){
  -            return false;
  -        }
  -        nodePointer.setIndex(position - 1);
           return nodeTest == null || nodePointer.testNode(nodeTest);
       }
   }
  
  
  
  1.3       +10 -5     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/UnionContext.java
  
  Index: UnionContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/UnionContext.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- UnionContext.java	21 Sep 2001 23:22:44 -0000	1.2
  +++ UnionContext.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/UnionContext.java,v 1.2 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/UnionContext.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -72,7 +72,7 @@
    * of a union operation like (a | b)
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class UnionContext extends EvalContext {
       private boolean startedSet = false;
  @@ -85,11 +85,16 @@
       }
   
       public NodePointer getCurrentNodePointer(){
  +        if (position == 0){
  +            if (!setPosition(1)){
  +                return null;
  +            }
  +        }
           return (NodePointer)list.get(position - 1);
       }
   
       public boolean setPosition(int position){
  -        this.position = position;
  +        super.setPosition(position);
           if (list == null){
               prepareList();
           }
  
  
  
  1.2       +26 -4     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java
  
  Index: ExpressionPath.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExpressionPath.java	23 Aug 2001 00:46:59 -0000	1.1
  +++ ExpressionPath.java	10 Apr 2002 03:40:20 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java,v 1.1 2001/08/23 00:46:59 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:59 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java,v 1.2 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -68,13 +68,18 @@
    * a path that starts with an expression like a function call: <code>getFoo(.)/bar</code>.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:59 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:20 $
    */
   public class ExpressionPath extends Path {
   
  +    public static final String BASIC_PREDICATES_HINT = "basicPredicatesHint";
  +
       private Expression expression;
       private Expression predicates[];
   
  +    private boolean basicKnown = false;
  +    private boolean basic;
  +
       public ExpressionPath(Expression expression, Expression[] predicates, Step[] steps){
           super(Expression.OP_EXPRESSION_PATH, steps);
           this.expression = expression;
  @@ -151,6 +156,23 @@
                   }
               }
           }
  +    }
  +
  +    /**
  +     * Recognized paths formatted as <code>$x[3]/foo[2]</code>.  The
  +     * evaluation of such "simple" paths is optimized and streamlined.
  +     */
  +    public Object getEvaluationHint(String hint){
  +        if (!hint.equals(BASIC_PREDICATES_HINT)){
  +            return super.getEvaluationHint(hint);
  +        }
  +
  +        if (!basicKnown){
  +            basicKnown = true;
  +            basic = super.getEvaluationHint(BASIC_PATH_HINT).equals(Boolean.TRUE) &&
  +                    areBasicPredicates(getPredicates());
  +        }
  +        return basic ? Boolean.TRUE : Boolean.FALSE;
       }
   
       public String toString(){
  
  
  
  1.2       +12 -4     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java
  
  Index: ExtensionFunction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExtensionFunction.java	23 Aug 2001 00:46:59 -0000	1.1
  +++ ExtensionFunction.java	10 Apr 2002 03:40:20 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java,v 1.1 2001/08/23 00:46:59 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:59 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java,v 1.2 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -67,7 +67,7 @@
    * Represents an element of the parse tree representing an extension function call.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:59 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:20 $
    */
   public class ExtensionFunction extends Operation {
   
  @@ -80,6 +80,14 @@
   
       public QName getFunctionName(){
           return functionName;
  +    }
  +
  +    /**
  +     * An extension function gets the current context, therefore it MAY be
  +     * context dependent.
  +     */
  +    public boolean computeContextDependent(){
  +        return true;
       }
   
       public String opCodeToString(){
  
  
  
  1.3       +4 -44     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/LocationPath.java
  
  Index: LocationPath.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/LocationPath.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- LocationPath.java	21 Sep 2001 23:22:44 -0000	1.2
  +++ LocationPath.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/LocationPath.java,v 1.2 2001/09/21 23:22:44 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:44 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/LocationPath.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -62,18 +62,14 @@
   package org.apache.commons.jxpath.ri.compiler;
   
   import java.util.*;
  -import org.apache.commons.jxpath.ri.Compiler;
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:44 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class LocationPath extends Path {
   
       private boolean absolute;
  -    public static final String BASIC_PATH_HINT = "basicPathHint";
  -    private boolean basicKnown = false;
  -    private boolean basic;
   
       public LocationPath(boolean absolute, Step[] steps){
           super(Expression.OP_LOCATION_PATH, steps);
  @@ -113,41 +109,5 @@
           }
           buffer.append(')');
           return buffer.toString();
  -    }
  -
  -    /**
  -     * Recognized predicated formatted as <code>[@name = <i>expr</i>]</code>
  -     */
  -    public Object getEvaluationHint(String hint){
  -        if (!hint.equals(BASIC_PATH_HINT)){
  -            return null;
  -        }
  -
  -        if (!basicKnown){
  -            basicKnown = true;
  -            basic = true;
  -            Step[] steps = getSteps();
  -            for (int i = 0; i < steps.length; i++){
  -                if (steps[i].getAxis() != Compiler.AXIS_CHILD ||
  -                        !(steps[i].getNodeTest() instanceof NodeNameTest) ||
  -                        ((NodeNameTest)steps[i].getNodeTest()).getNodeName().getName().equals("*")){
  -                    basic = false;
  -                    break;
  -                }
  -                Expression predicates[] = steps[i].getPredicates();
  -                if (predicates != null && predicates.length != 0){
  -                    if (predicates.length != 1){
  -                        basic = false;
  -                        break;
  -                    }
  -                    if (predicates[0].getEvaluationHint(CoreOperation.DYNAMIC_PROPERTY_ACCESS_HINT) == null &&
  -                            predicates[0].isContextDependent()){
  -                        basic = false;
  -                        break;
  -                    }
  -                }
  -            }
  -        }
  -        return basic ? Boolean.TRUE : Boolean.FALSE;
       }
   }
  
  
  
  1.2       +60 -4     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java
  
  Index: Path.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Path.java	23 Aug 2001 00:46:59 -0000	1.1
  +++ Path.java	10 Apr 2002 03:40:20 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java,v 1.1 2001/08/23 00:46:59 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:46:59 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java,v 1.2 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -62,14 +62,18 @@
   package org.apache.commons.jxpath.ri.compiler;
   
   import java.util.*;
  +import org.apache.commons.jxpath.ri.Compiler;
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:46:59 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:20 $
    */
   public class Path extends Expression {
   
       private Step[] steps;
  +    public static final String BASIC_PATH_HINT = "basicPathHint";
  +    private boolean basicKnown = false;
  +    private boolean basic;
   
       public Path(int typeCode, Step[] steps){
           super(typeCode);
  @@ -112,5 +116,57 @@
                   }
               }
           }
  +    }
  +
  +    /**
  +     * Recognized paths formatted as <code>foo/bar[3]/baz[@name = 'biz']</code>.  The
  +     * evaluation of such "simple" paths is optimized and streamlined.
  +     */
  +    public Object getEvaluationHint(String hint){
  +        if (!hint.equals(BASIC_PATH_HINT)){
  +            return null;
  +        }
  +
  +        if (!basicKnown){
  +            basicKnown = true;
  +            basic = true;
  +            Step[] steps = getSteps();
  +            for (int i = 0; i < steps.length; i++){
  +//                System.err.println("STEP: " + steps[i]);
  +                if (steps[i].getAxis() != Compiler.AXIS_CHILD ||
  +                        !(steps[i].getNodeTest() instanceof NodeNameTest) ||
  +                        ((NodeNameTest)steps[i].getNodeTest()).getNodeName().getName().equals("*")){
  +                    basic = false;
  +                    break;
  +                }
  +                Expression predicates[] = steps[i].getPredicates();
  +                basic = basic && areBasicPredicates(predicates);
  +            }
  +        }
  +        return basic ? Boolean.TRUE : Boolean.FALSE;
  +    }
  +
  +    protected boolean areBasicPredicates(Expression predicates[]){
  +        if (predicates != null && predicates.length != 0){
  +            boolean firstIndex = true;
  +            for (int i = 0; i < predicates.length; i++){
  +                Expression dyn = (Expression)predicates[i].getEvaluationHint(CoreOperation.DYNAMIC_PROPERTY_ACCESS_HINT);
  +                if (dyn != null){
  +                    if (dyn.isContextDependent()){
  +                        return false;
  +                    }
  +                }
  +                else if (predicates[i].isContextDependent()){
  +                    return false;
  +                }
  +                else {
  +                    if (!firstIndex){
  +                        return false;
  +                    }
  +                    firstIndex = false;
  +                }
  +            }
  +        }
  +        return true;
       }
   }
  
  
  
  1.4       +62 -9     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPropertyPointer.java
  
  Index: BeanPropertyPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPropertyPointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- BeanPropertyPointer.java	21 Sep 2001 23:22:45 -0000	1.3
  +++ BeanPropertyPointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPropertyPointer.java,v 1.3 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPropertyPointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,9 +73,10 @@
    * Pointer pointing to a property of a JavaBean.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class BeanPropertyPointer extends PropertyPointer {
  +    private String propertyName;
       private JXPathBeanInfo beanInfo;
       private PropertyDescriptor propertyDescriptors[];
       private PropertyDescriptor propertyDescriptor;
  @@ -111,6 +112,7 @@
        * Select a property by name
        */
       public void setPropertyName(String propertyName){
  +        this.propertyName = propertyName;
           setPropertyIndex(UNSPECIFIED_PROPERTY);
           String[] names = getPropertyNames();
           for (int i = 0; i < names.length; i++){
  @@ -168,27 +170,78 @@
           }
           else {
               if (index == WHOLE_COLLECTION){
  -                value = PropertyAccessHelper.getValue(getBean(), getPropertyDescriptor());
  +                value = PropertyAccessHelper.getValue(getBean(), pd);
               }
               else {
  -                value = PropertyAccessHelper.getValue(getBean(), getPropertyDescriptor(), index);
  +                value = PropertyAccessHelper.getValue(getBean(), pd, index);
               }
           }
           return value;
       }
   
  +    protected boolean isActualProperty(){
  +        return getPropertyDescriptor() != null;
  +    }
  +
       /**
        * If index == WHOLE_COLLECTION, change the value of the property, otherwise
        * change the value of the index'th element of the collection
        * represented by the property.
        */
       public void setValue(Object value){
  +        PropertyDescriptor pd = getPropertyDescriptor();
  +        if (pd == null){
  +            throw new RuntimeException("Cannot set property: " + asPath() + " - no such property");
  +        }
  +
           if (index == WHOLE_COLLECTION){
  -            PropertyAccessHelper.setValue(getBean(), getPropertyDescriptor(), value);
  +            PropertyAccessHelper.setValue(getBean(), pd, value);
           }
           else {
  -            PropertyAccessHelper.setValue(getBean(), getPropertyDescriptor(), index, value);
  +            PropertyAccessHelper.setValue(getBean(), pd, index, value);
  +        }
  +    }
  +
  +    public NodePointer createPath(JXPathContext context){
  +        if (getValue() == null){
  +            AbstractFactory factory = getAbstractFactory(context);
  +            int inx = (index == WHOLE_COLLECTION ? 0 : index);
  +            if (!factory.createObject(context, this, getBean(), getPropertyName(), inx)){
  +                throw new RuntimeException("Factory could not create an object for path: " + asPath());
  +            }
  +        }
  +        return this;
  +    }
  +
  +    public NodePointer createPath(JXPathContext context, int index){
  +        setIndexExpandingCollection(context, index);
  +        return createPath(context);
  +    }
  +
  +    public void createPath(JXPathContext context, int index, Object value){
  +        setIndexExpandingCollection(context, index);
  +        setValue(value);
  +    }
  +
  +    private void setIndexExpandingCollection(JXPathContext context, int index){
  +        PropertyDescriptor pd = getPropertyDescriptor();
  +        if (pd == null){
  +            throw new RuntimeException("Cannot create path: " + asPath() +
  +                    " - property '" + getPropertyName() + "' does not exist");
  +        }
  +
  +        if (index < 0){
  +            throw new RuntimeException("Index is less than 1: " + asPath());
  +        }
  +
  +        if (index >= getLength()){
  +            AbstractFactory factory = getAbstractFactory(context);
  +            if (!factory.expandCollection(context, this, getBean(), getPropertyName(), index + 1)){
  +                throw new RuntimeException("Factory could not expand collection for path " + asPath() +
  +                    " to size " + (index + 1));
  +            }
           }
  +        setIndex(index);
       }
   
       /**
  @@ -197,7 +250,7 @@
       public String getPropertyName(){
           PropertyDescriptor pd = getPropertyDescriptor();
           if (pd == null){
  -            return "*";
  +            return propertyName != null ? propertyName : "*";
           }
           return pd.getName();
       }
  
  
  
  1.4       +5 -5      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointer.java
  
  Index: ContainerPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ContainerPointer.java	26 Sep 2001 01:21:54 -0000	1.3
  +++ ContainerPointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointer.java,v 1.3 2001/09/26 01:21:54 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/26 01:21:54 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -76,7 +76,7 @@
    * itself.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/26 01:21:54 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class ContainerPointer extends NodePointer {
       private Container container;
  @@ -112,7 +112,7 @@
           container.setValue(value);
       }
   
  -    private NodePointer getValuePointer(){
  +    public NodePointer getValuePointer(){
           if (valuePointer == null){
               Object value = getValue();
               valuePointer = NodePointer.createNodePointer(this, null, value);
  
  
  
  1.3       +8 -4      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMAttributePointer.java
  
  Index: DOMAttributePointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMAttributePointer.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- DOMAttributePointer.java	21 Sep 2001 23:22:45 -0000	1.2
  +++ DOMAttributePointer.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMAttributePointer.java,v 1.2 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMAttributePointer.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,7 +74,7 @@
    * A Pointer that points to a DOM node.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class DOMAttributePointer extends NodePointer {
       private Attr attr;
  @@ -113,6 +113,10 @@
               return null;
           }
           return value;
  +    }
  +
  +    public boolean isActual(){
  +        return true;
       }
   
       public boolean isLeaf(){
  
  
  
  1.4       +8 -4      jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMNodePointer.java
  
  Index: DOMNodePointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMNodePointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DOMNodePointer.java	26 Sep 2001 01:21:54 -0000	1.3
  +++ DOMNodePointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMNodePointer.java,v 1.3 2001/09/26 01:21:54 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/26 01:21:54 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMNodePointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,7 +74,7 @@
    * A Pointer that points to a DOM node.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/26 01:21:54 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class DOMNodePointer extends NodePointer {
       private Node node;
  @@ -269,6 +269,10 @@
   
       public Object getValue(){
           return node;
  +    }
  +
  +    public boolean isActual(){
  +        return true;
       }
   
       public boolean isCollection(){
  
  
  
  1.4       +54 -4     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPropertyPointer.java
  
  Index: DynamicPropertyPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPropertyPointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DynamicPropertyPointer.java	21 Sep 2001 23:22:45 -0000	1.3
  +++ DynamicPropertyPointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPropertyPointer.java,v 1.3 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPropertyPointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -73,7 +73,7 @@
    * Pointer pointing to a property of an object with dynamic properties.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class DynamicPropertyPointer extends PropertyPointer {
       private DynamicPropertyHandler handler;
  @@ -213,6 +213,14 @@
       }
   
       /**
  +     * A dynamic property is always considered actual - all keys are apparently
  +     * existing with possibly the value of null.
  +     */
  +    protected boolean isActualProperty(){
  +        return true;
  +    }
  +
  +    /**
        * If index == WHOLE_COLLECTION, change the value of the property, otherwise
        * change the value of the index'th element of the collection
        * represented by the property.
  @@ -224,6 +232,48 @@
           else {
               PropertyAccessHelper.setValue(handler.getProperty(getBean(), getPropertyName()), index, value);
           }
  +    }
  +
  +    public void createPath(JXPathContext context, Object value){
  +        createPath(context, index, value);
  +    }
  +
  +    public void createPath(JXPathContext context, int index, Object value){
  +        if (index == WHOLE_COLLECTION){
  +            handler.setProperty(getBean(), getPropertyName(), value);
  +        }
  +        else {
  +            Object collection = getBaseValue();
  +            if (collection == null){
  +                AbstractFactory factory = getAbstractFactory(context);
  +                if (!factory.createObject(context, this, getBean(), getPropertyName(), 0)){
  +                    throw new RuntimeException("Factory could not create an collection for path: " + asPath());
  +                }
  +                collection = getBaseValue();
  +            }
  +
  +            if (index < 0){
  +                throw new RuntimeException("Index is less than 1: " + asPath());
  +            }
  +
  +            if (index >= getLength()){
  +                collection = PropertyAccessHelper.expandCollection(collection, index + 1);
  +                handler.setProperty(getBean(), getPropertyName(), collection);
  +            }
  +
  +            PropertyAccessHelper.setValue(collection, index, value);
  +        }
  +    }
  +
  +    public NodePointer createPath(JXPathContext context){
  +        if (getValue() == null){
  +            AbstractFactory factory = getAbstractFactory(context);
  +            int inx = (index == WHOLE_COLLECTION ? 0 : index);
  +            if (!factory.createObject(context, this, getBean(), getPropertyName(), inx)){
  +                throw new RuntimeException("Factory could not create an object for path: " + asPath());
  +            }
  +        }
  +        return this;
       }
   
       public String asPath(){
  
  
  
  1.5       +102 -33   jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointer.java
  
  Index: NodePointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointer.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- NodePointer.java	26 Sep 2001 01:21:54 -0000	1.4
  +++ NodePointer.java	10 Apr 2002 03:40:20 -0000	1.5
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointer.java,v 1.4 2001/09/26 01:21:54 dmitri Exp $
  - * $Revision: 1.4 $
  - * $Date: 2001/09/26 01:21:54 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointer.java,v 1.5 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.5 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -62,6 +62,7 @@
   package org.apache.commons.jxpath.ri.pointers;
   
   import org.apache.commons.jxpath.*;
  +import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
   import org.apache.commons.jxpath.ri.Compiler;
   import org.apache.commons.jxpath.ri.compiler.*;
   
  @@ -71,10 +72,10 @@
   import org.w3c.dom.Node;
   
   /**
  - * Common superclass for Poitners of all kinds.
  + * Common superclass for Pointers of all kinds.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.4 $ $Date: 2001/09/26 01:21:54 $
  + * @version $Revision: 1.5 $ $Date: 2002/04/10 03:40:20 $
    */
   public abstract class NodePointer implements Pointer, Cloneable {
   
  @@ -86,42 +87,28 @@
           if (bean == null){
               return new NullPointer(name, locale);
           }
  -        if (bean instanceof Node){
  -            return new DOMNodePointer((Node)bean, locale);
  -        }
  -        if (bean instanceof Container){
  -            return new ContainerPointer((Container)bean, locale);
  -        }
  -
  -        JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
  -        if (bi.isDynamic()){
  -            DynamicPropertyHandler handler = PropertyAccessHelper.getDynamicPropertyHandler(bi.getDynamicPropertyHandlerClass());
  -            return new DynamicPointer(name, bean, handler, locale);
  -        }
  -        else {
  -            return new BeanPointer(name, bean, bi, locale);
  +        NodePointerFactory[] factories = JXPathContextReferenceImpl.getNodePointerFactories();
  +        for (int i = 0; i < factories.length; i++){
  +            NodePointer pointer = factories[i].createNodePointer(name, bean, locale);
  +            if (pointer != null){
  +                return pointer;
  +            }
           }
  +        throw new RuntimeException("Could not allocate a NodePointer for object of " + bean.getClass());
       }
   
       public static NodePointer createNodePointer(NodePointer parent, QName name, Object bean){
           if (bean == null){
               return new NullPointer(parent, name);
           }
  -        if (bean instanceof Node){
  -            return new DOMNodePointer(parent, (Node)bean);
  -        }
  -        if (bean instanceof Container){
  -            return new ContainerPointer(parent, (Container)bean);
  -        }
  -
  -        JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
  -        if (bi.isDynamic()){
  -            DynamicPropertyHandler handler = PropertyAccessHelper.getDynamicPropertyHandler(bi.getDynamicPropertyHandlerClass());
  -            return new DynamicPointer(parent, name, bean, handler);
  -        }
  -        else {
  -            return new BeanPointer(parent, name, bean, bi);
  +        NodePointerFactory[] factories = JXPathContextReferenceImpl.getNodePointerFactories();
  +        for (int i = 0; i < factories.length; i++){
  +            NodePointer pointer = factories[i].createNodePointer(parent, name, bean);
  +            if (pointer != null){
  +                return pointer;
  +            }
           }
  +        throw new RuntimeException("Could not allocate a NodePointer for object of " + bean.getClass());
       }
   
       /**
  @@ -233,6 +220,8 @@
       }
   
       public void setIndex(int index){
  +//        System.err.println("SETTING: " + this.getClass() + " " + index);
  +//        new Exception().printStackTrace();
           this.index = index;
       }
   
  @@ -249,11 +238,83 @@
           return PropertyAccessHelper.getLength(value);
       }
   
  +    /**
  +     * If this pointer manages a transparent container, like a variable,
  +     * this method returns the ponter to the contents.
  +     */
  +    public NodePointer getValuePointer(){
  +        if (this instanceof PropertyOwnerPointer){
  +            return this;
  +        }
  +        return null;
  +    }
  +
  +    /**
  +     * An actual pointer points to an existing part of an object graph, even
  +     * if it is null. A non-actual pointer represents a part that does not exist
  +     * at all.
  +     * For instance consider the pointer "/address/street".
  +     * If both <em>address</em> and <em>street</em> are not null, the pointer is actual.
  +     * If <em>address</em> is not null, but <em>street</em> is null, the pointer is still actual.
  +     * If <em>address</em> is null, the pointer is not actual.
  +     * (In JavaBeans) if <em>address</em> is not a property of the root bean, a Pointer
  +     * for this path cannot be obtained at all - actual or otherwise.
  +     */
  +    public boolean isActual(){
  +        if (index == WHOLE_COLLECTION){
  +            return true;
  +        }
  +        else {
  +            return index >= 0 && index < getLength();
  +        }
  +    }
  +
  +
       public abstract QName getName();
       public abstract Object getBaseValue();
       public abstract void setValue(Object value);
       public abstract boolean testNode(NodeTest nodeTest);
   
  +    /**
  +     *  Called directly by JXPathContext. Must create path and
  +     *  set value.
  +     */
  +    public void createPath(JXPathContext context, Object value){
  +        setValue(value);
  +    }
  +
  +    /**
  +     * Called by a child pointer if that child needs to assign the value
  +     * supplied in the createPath(context, value) call to a non-existent
  +     * collection element. This method must expand the collection and
  +     * assign the element.
  +     */
  +    public void createPath(JXPathContext context, int index, Object value){
  +        throw new RuntimeException("Cannot expand collection for path " + asPath() +
  +                ", or it is not a collection at all");
  +    }
  +
  +    /**
  +     * Called by a child pointer when it needs to create a parent object.
  +     * Must create an object described by this pointer and return
  +     * a new pointer that properly describes the new object.
  +     */
  +    public NodePointer createPath(JXPathContext context){
  +        throw new RuntimeException("Cannot create an object for path " + asPath() +
  +                ", operation is not allowed for this type of node");
  +    }
  +
  +    /**
  +     * Called by a child pointer when it needs to create a parent object
  +     * for a non-existent collection element.  Must expand the collection,
  +     * create an element object and return a new pointer describing the
  +     * newly created element.
  +     */
  +    public NodePointer createPath(JXPathContext context, int index){
  +        throw new RuntimeException("Cannot create an object for path " + asPath() +
  +                ", operation is not allowed for this type of node");
  +    }
  +
       public Locale getLocale(){
           if (locale == null){
               if (parent != null){
  @@ -304,5 +365,13 @@
   
       public String toString(){
           return asPath();
  +    }
  +
  +    protected AbstractFactory getAbstractFactory(JXPathContext context){
  +        AbstractFactory factory = context.getFactory();
  +        if (factory == null){
  +            throw new RuntimeException("Factory is not set on the JXPathContext - cannot create path: " + asPath());
  +        }
  +        return factory;
       }
   }
  
  
  
  1.5       +24 -4     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPointer.java
  
  Index: NullPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPointer.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- NullPointer.java	26 Sep 2001 01:21:54 -0000	1.4
  +++ NullPointer.java	10 Apr 2002 03:40:20 -0000	1.5
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPointer.java,v 1.4 2001/09/26 01:21:54 dmitri Exp $
  - * $Revision: 1.4 $
  - * $Date: 2001/09/26 01:21:54 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPointer.java,v 1.5 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.5 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -71,7 +71,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.4 $ $Date: 2001/09/26 01:21:54 $
  + * @version $Revision: 1.5 $ $Date: 2002/04/10 03:40:20 $
    */
   public class NullPointer extends PropertyOwnerPointer {
       private QName name;
  @@ -99,6 +99,26 @@
   
       public void setValue(Object value){
           throw new UnsupportedOperationException("Cannot setValue of an object that is not some other object's property");
  +    }
  +
  +    public boolean isActual(){
  +        return false;
  +    }
  +
  +    public void createPath(JXPathContext context, Object value){
  +        if (parent != null){
  +            parent.createPath(context, value);
  +        }
  +        else {
  +            throw new UnsupportedOperationException("Cannot create the root object");
  +        }
  +    }
  +
  +    public NodePointer createPath(JXPathContext context){
  +        if (parent != null){
  +            return parent.createPath(context);
  +        }
  +        throw new UnsupportedOperationException("Cannot create the root object");
       }
   
       public int hashCode(){
  
  
  
  1.4       +100 -4    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPropertyPointer.java
  
  Index: NullPropertyPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPropertyPointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- NullPropertyPointer.java	21 Sep 2001 23:22:45 -0000	1.3
  +++ NullPropertyPointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPropertyPointer.java,v 1.3 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullPropertyPointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -71,11 +71,12 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class NullPropertyPointer extends PropertyPointer {
   
       private String propertyName = "*";
  +    private boolean dynamic = false;
   
       /**
        */
  @@ -98,11 +99,67 @@
           return null;
       }
   
  +    protected boolean isActualProperty(){
  +        return false;
  +    }
  +
  +    public boolean isActual(){
  +        return false;
  +    }
  +
       public void setValue(Object value){
           throw new RuntimeException("Cannot set property " + asPath() +
               ", the target object is null");
       }
   
  +    public void createPath(JXPathContext context, Object value){
  +         createParentPath(context).setValue(value);
  +    }
  +
  +    public void createPath(JXPathContext context, int index, Object value){
  +        NodePointer pointer = createParentPath(context);
  +        pointer.setIndex(index);
  +        pointer.createPath(context, value);
  +    }
  +
  +    public NodePointer createPath(JXPathContext context, int index){
  +        NodePointer pointer = createParentPath(context);
  +        return pointer.createPath(context, index);
  +    }
  +
  +    public NodePointer createPath(JXPathContext context){
  +        NodePointer pointer = createParentPath(context);
  +        NodePointer result = pointer.createPath(context);
  +        if (!result.isActual()){
  +            throw new RuntimeException("Could not create an object for path " + asPath() +
  +                " that could have property " + getPropertyName());
  +        }
  +        return result;
  +    }
  +
  +    private NodePointer createParentPath(JXPathContext context){
  +        NodePointer pointer = parent.createPath(context);
  +        while (true){
  +            if (pointer instanceof VariablePointer){
  +                pointer = ((VariablePointer)pointer).getValuePointer();
  +            }
  +            else if (pointer instanceof ContainerPointer){
  +                pointer = ((ContainerPointer)pointer).getValuePointer();
  +            }
  +            else {
  +                break;
  +            }
  +        }
  +        if (!(pointer instanceof PropertyOwnerPointer)){
  +            throw new RuntimeException("Could not create an object for path " + asPath() +
  +                " that has property " + getPropertyName());
  +        }
  +        PropertyPointer prop = ((PropertyOwnerPointer)pointer).getPropertyPointer();
  +        prop.setPropertyName(propertyName);
  +        prop.setIndex(index);
  +        return prop;
  +    }
  +
       public String getPropertyName(){
           return propertyName;
       }
  @@ -111,11 +168,50 @@
           this.propertyName = propertyName;
       }
   
  +    public void setDynamic(boolean flag){
  +        dynamic = flag;
  +    }
  +
  +    public boolean isCollection(){
  +        return getIndex() != WHOLE_COLLECTION;
  +    }
  +
       public int getPropertyCount(){
           return 0;
       }
   
       public String[] getPropertyNames(){
           return new String[0];
  +    }
  +
  +    public String asPath(){
  +        if (!dynamic){
  +            return super.asPath();
  +        }
  +        else {
  +            StringBuffer buffer = new StringBuffer();
  +            buffer.append(getParent().asPath());
  +            buffer.append("[@name='");
  +            buffer.append(escape(getPropertyName()));
  +            buffer.append("']");
  +            if (index != WHOLE_COLLECTION){
  +                buffer.append('[').append(index + 1).append(']');
  +            }
  +            return buffer.toString();
  +        }
  +    }
  +
  +    private String escape(String string){
  +        int index = string.indexOf('\'');
  +        while (index != -1){
  +            string = string.substring(0, index) + "&apos;" + string.substring(index + 1);
  +            index = string.indexOf('\'');
  +        }
  +        index = string.indexOf('\"');
  +        while (index != -1){
  +            string = string.substring(0, index) + "&quot;" + string.substring(index + 1);
  +            index = string.indexOf('\"');
  +        }
  +        return string;
       }
   }
  
  
  
  1.3       +54 -7     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyAccessHelper.java
  
  Index: PropertyAccessHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyAccessHelper.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PropertyAccessHelper.java	9 Sep 2001 00:52:04 -0000	1.2
  +++ PropertyAccessHelper.java	10 Apr 2002 03:40:20 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyAccessHelper.java,v 1.2 2001/09/09 00:52:04 dmitri Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/09/09 00:52:04 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyAccessHelper.java,v 1.3 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -64,6 +64,7 @@
   import org.apache.commons.jxpath.*;
   import org.apache.commons.jxpath.ri.Compiler;
   import org.apache.commons.jxpath.ri.compiler.*;
  +import org.apache.commons.jxpath.functions.Types;
   
   import java.lang.reflect.*;
   import java.util.*;
  @@ -71,7 +72,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.2 $ $Date: 2001/09/09 00:52:04 $
  + * @version $Revision: 1.3 $ $Date: 2002/04/10 03:40:20 $
    */
   public class PropertyAccessHelper {
       private static Map dynamicPropertyHandlerMap = new HashMap();
  @@ -117,6 +118,27 @@
           }
       }
   
  +    public static Object expandCollection(Object collection, int size){
  +        if (collection == null){
  +            return null;
  +        }
  +        else if (collection.getClass().isArray()){
  +            Object bigger = Array.newInstance(collection.getClass().getComponentType(), size);
  +            System.arraycopy(collection, 0, bigger, 0, Array.getLength(collection));
  +            return bigger;
  +        }
  +        else if (collection instanceof Collection){
  +            while (((Collection)collection).size() < size){
  +                ((Collection)collection).add(null);
  +            }
  +            return collection;
  +        }
  +        else {
  +            throw new RuntimeException("Cannot turn " + collection.getClass().getName() +
  +                    " into a collection of size " + size);
  +        }
  +    }
  +
       public static Object getValue(Object bean, PropertyDescriptor propertyDescriptor, int index){
           if (propertyDescriptor instanceof IndexedPropertyDescriptor){
               Object value;
  @@ -143,7 +165,9 @@
                   IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)propertyDescriptor;
                   Method method = ipd.getIndexedWriteMethod();
                   if (method != null){
  -                    method.invoke(bean, new Object[]{new Integer(index), value});
  +                    method.invoke(bean,
  +                        new Object[]{new Integer(index),
  +                                     convert(value, ipd.getIndexedPropertyType())});
                       return;
                   }
               }
  @@ -160,16 +184,29 @@
           Object value = collection;
           if (collection != null){
               if (collection.getClass().isArray()){
  +                if (index < 0 || index >= Array.getLength(collection)){
  +                    return null;
  +                }
                   value = Array.get(collection, index);
               }
               else if (collection instanceof List){
  +                if (index < 0 || index >= ((List)collection).size()){
  +                    return null;
  +                }
                   value = ((List)collection).get(index);
               }
               else if (collection instanceof Collection){
  +                int i = 0;
                   Iterator it = ((Collection)collection).iterator();
  -                for (int i = 0; i <= index; i++){
  +                for (; i < index; i++){
  +                    it.next();
  +                }
  +                if (it.hasNext()){
                       value = it.next();
                   }
  +                else {
  +                    value = null;
  +                }
               }
           }
           return value;
  @@ -178,7 +215,7 @@
       public static void setValue(Object collection, int index, Object value){
           if (collection != null){
               if (collection.getClass().isArray()){
  -                Array.set(collection, index, value);
  +                Array.set(collection, index, convert(value, collection.getClass().getComponentType()));
               }
               else if (collection instanceof List){
                   ((List)collection).set(index, value);
  @@ -213,12 +250,22 @@
               if (method == null){
                   throw new RuntimeException("No write method");
               }
  +            value = convert(value, propertyDescriptor.getPropertyType());
               value = method.invoke(bean, new Object[]{value});
           }
           catch (Exception ex){
               throw new RuntimeException("Cannot modify property: " + propertyDescriptor.getName() +
                   ", " + ex);
           }
  +    }
  +
  +    private static Object convert(Object value, Class type){
  +        if (!Types.canConvert(value, type)){
  +            throw new RuntimeException("Cannot convert value of class " +
  +                    (value == null ? "null" : value.getClass().getName()) +
  +                    " to type " + type);
  +        }
  +        return Types.convert(value, type);
       }
   
       /**
  
  
  
  1.4       +21 -5     jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyPointer.java
  
  Index: PropertyPointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyPointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PropertyPointer.java	21 Sep 2001 23:22:45 -0000	1.3
  +++ PropertyPointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyPointer.java,v 1.3 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/PropertyPointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -72,7 +72,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public abstract class PropertyPointer extends PropertyOwnerPointer {
       protected int propertyIndex = UNSPECIFIED_PROPERTY;
  @@ -114,6 +114,17 @@
   
       public abstract String[] getPropertyNames();
   
  +    protected abstract boolean isActualProperty();
  +
  +    public boolean isActual(){
  +        if (!isActualProperty()){
  +            return false;
  +        }
  +
  +        return super.isActual();
  +    }
  +
  +
       /**
        * Returns a NodePointer that can be used to access the currently
        * selected property value.
  @@ -152,7 +163,12 @@
   
       public String toString(){
           StringBuffer buffer = new StringBuffer();
  -        buffer.append(getBean().getClass().getName());
  +        if (getBean() == null){
  +            buffer.append("null");
  +        }
  +        else {
  +            buffer.append(getBean().getClass().getName());
  +        }
           buffer.append('@');
           buffer.append(System.identityHashCode(getBean()));
           buffer.append('.');
  
  
  
  1.4       +120 -9    jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/VariablePointer.java
  
  Index: VariablePointer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/VariablePointer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- VariablePointer.java	21 Sep 2001 23:22:45 -0000	1.3
  +++ VariablePointer.java	10 Apr 2002 03:40:20 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/VariablePointer.java,v 1.3 2001/09/21 23:22:45 dmitri Exp $
  - * $Revision: 1.3 $
  - * $Date: 2001/09/21 23:22:45 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/VariablePointer.java,v 1.4 2002/04/10 03:40:20 dmitri Exp $
  + * $Revision: 1.4 $
  + * $Date: 2002/04/10 03:40:20 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -74,17 +74,25 @@
    * Pointer to a context variable.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.3 $ $Date: 2001/09/21 23:22:45 $
  + * @version $Revision: 1.4 $ $Date: 2002/04/10 03:40:20 $
    */
   public class VariablePointer extends NodePointer {
       private Variables variables;
       private QName name;
       private NodePointer valuePointer;
  +    private boolean actual;
   
       public VariablePointer(Variables variables, QName name){
           super(null);
           this.variables = variables;
           this.name = name;
  +        actual = true;
  +    }
  +
  +    public VariablePointer(QName name){
  +        super(null);
  +        this.name = name;
  +        actual = false;
       }
   
       public QName getName(){
  @@ -92,6 +100,9 @@
       }
   
       public Object getBaseValue(){
  +        if (!actual){
  +            throw new RuntimeException("Undefined variable: " + name);
  +        }
           return variables.getVariable(name.getName());
       }
   
  @@ -104,20 +115,115 @@
       }
   
       public void setValue(Object value){
  +        if (!actual){
  +            throw new RuntimeException("Cannot set undefined variable: " + name);
  +        }
           valuePointer = null;
  -        variables.declareVariable(name.getName(), value);
  +        if (index != WHOLE_COLLECTION){
  +            Object collection = getBaseValue();
  +            PropertyAccessHelper.setValue(collection, index, value);
  +        }
  +        else {
  +            variables.declareVariable(name.getName(), value);
  +        }
  +    }
  +
  +    public boolean isActual(){
  +        return actual;
       }
   
  -    private NodePointer getValuePointer(){
  +    public NodePointer getValuePointer(){
           if (valuePointer == null){
  -            Object value = getValue();
  +            Object value = null;
  +            if (actual){
  +                value = getValue();
  +            }
               valuePointer = NodePointer.createNodePointer(this, null, value);
  +//            System.err.println("VALUE POINTER: " + valuePointer.getClass());
           }
           return valuePointer;
       }
   
  +    public int getLength(){
  +        if (actual){
  +            return super.getLength();
  +        }
  +        return 0;
  +    }
  +
  +    public void createPath(JXPathContext context, Object value){
  +        if (actual){
  +            setValue(value);
  +            return;
  +        }
  +        createPath(context).setValue(value);
  +    }
  +
  +    public NodePointer createPath(JXPathContext context){
  +        if (!actual){
  +            AbstractFactory factory = getAbstractFactory(context);
  +            if (!factory.declareVariable(context, name.toString())){
  +                throw new RuntimeException("Factory cannot define variable '" + name + "' for path: " + asPath());
  +            }
  +            findVariables(context);
  +            // Assert: actual == true
  +        }
  +        return this;
  +    }
  +
  +    public NodePointer createPath(JXPathContext context, int index){
  +        Object collection = createCollection(context, index);
  +        AbstractFactory factory = getAbstractFactory(context);
  +        if (!factory.createObject(context, this, collection, name.toString(), index)){
  +            throw new RuntimeException("Factory could not create collection element for path: " + asPath());
  +        }
  +        setIndex(index);
  +        return this;
  +    }
  +
  +    /**
  +     */
  +    public void createPath(JXPathContext context, int index, Object value){
  +        Object collection = createCollection(context, index);
  +        PropertyAccessHelper.setValue(collection, index, value);
  +    }
  +
  +    private Object createCollection(JXPathContext context, int index){
  +        createPath(context);
  +
  +        Object collection = getBaseValue();
  +        if (collection == null){
  +            throw new RuntimeException("Factory did not assign a collection to variable '" + name + "' for path: " + asPath());
  +        }
  +
  +        if (index < 0){
  +            throw new RuntimeException("Index is less than 1: " + asPath());
  +        }
  +
  +        if (index >= getLength()){
  +            collection = PropertyAccessHelper.expandCollection(collection, index + 1);
  +            variables.declareVariable(name.toString(), collection);
  +        }
  +
  +        return collection;
  +    }
  +
  +    protected void findVariables(JXPathContext context){
  +        valuePointer = null;
  +        JXPathContext varCtx = context;
  +        while (varCtx != null){
  +            variables = varCtx.getVariables();
  +            if (variables.isDeclaredVariable(name.toString())){
  +                actual = true;
  +                break;
  +            }
  +            varCtx = varCtx.getParentContext();
  +            variables = null;
  +        }
  +    }
  +
       public int hashCode(){
  -        return System.identityHashCode(variables) + name.hashCode() + index;
  +        return (actual ? System.identityHashCode(variables): 0) + name.hashCode() + index;
       }
   
       public boolean equals(Object object){
  @@ -139,7 +245,12 @@
           StringBuffer buffer = new StringBuffer();
           buffer.append('$');
           buffer.append(name);
  -        if (index != WHOLE_COLLECTION && isCollection()){
  +        if (!actual){
  +            if (index != WHOLE_COLLECTION){
  +                buffer.append('[').append(index + 1).append(']');
  +            }
  +        }
  +        else if (index != WHOLE_COLLECTION && (getValue() == null || isCollection())){
               buffer.append('[').append(index + 1).append(']');
           }
           return buffer.toString();
  
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPointerFactory.java
  
  Index: BeanPointerFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/BeanPointerFactory.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.*;
  import org.apache.commons.jxpath.ri.compiler.QName;
  import java.util.*;
  
  /**
   * Implements NodePointerFactory for JavaBeans.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public class BeanPointerFactory implements NodePointerFactory {
  
      public static final int BEAN_POINTER_FACTORY_ORDER = 900;
  
      public int getOrder(){
          return BEAN_POINTER_FACTORY_ORDER;
      }
  
      public NodePointer createNodePointer(QName name, Object bean, Locale locale){
          JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
          return new BeanPointer(name, bean, bi, locale);
      }
  
      public NodePointer createNodePointer(NodePointer parent, QName name, Object bean){
          JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
          return new BeanPointer(parent, name, bean, bi);
      }
  }
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointerFactory.java
  
  Index: ContainerPointerFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/ContainerPointerFactory.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.Container;
  import org.apache.commons.jxpath.ri.compiler.QName;
  import java.util.*;
  
  /**
   * Implements NodePointerFactory for Container objects.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public class ContainerPointerFactory implements NodePointerFactory {
  
      public static final int CONTAINER_POINTER_FACTORY_ORDER = 200;
  
      public int getOrder(){
          return CONTAINER_POINTER_FACTORY_ORDER;
      }
  
      public NodePointer createNodePointer(QName name, Object bean, Locale locale){
          if (bean instanceof Container){
              return new ContainerPointer((Container)bean, locale);
          }
          return null;
      }
  
      public NodePointer createNodePointer(NodePointer parent, QName name, Object bean){
          if (bean instanceof Container){
              return new ContainerPointer(parent, (Container)bean);
          }
          return null;
      }
  }
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMPointerFactory.java
  
  Index: DOMPointerFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DOMPointerFactory.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.ri.compiler.QName;
  import java.util.*;
  import org.w3c.dom.Node;
  
  /**
   * Implements NodePointerFactory for DOM elements.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public class DOMPointerFactory implements NodePointerFactory {
  
      public static final int DOM_POINTER_FACTORY_ORDER = 100;
  
      public int getOrder(){
          return DOM_POINTER_FACTORY_ORDER;
      }
  
      public NodePointer createNodePointer(QName name, Object bean, Locale locale){
          if (bean instanceof Node){
              return new DOMNodePointer((Node)bean, locale);
          }
          return null;
      }
  
      public NodePointer createNodePointer(NodePointer parent, QName name, Object bean){
          if (bean instanceof Node){
              return new DOMNodePointer(parent, (Node)bean);
          }
          return null;
      }
  }
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPointerFactory.java
  
  Index: DynamicPointerFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/DynamicPointerFactory.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.*;
  import org.apache.commons.jxpath.ri.compiler.QName;
  import java.util.*;
  
  /**
   * Implements NodePointerFactory for Dynamic classes like Map.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public class DynamicPointerFactory implements NodePointerFactory {
  
      public static final int DYNAMIC_POINTER_FACTORY_ORDER = 800;
  
      public int getOrder(){
          return DYNAMIC_POINTER_FACTORY_ORDER;
      }
  
      public NodePointer createNodePointer(QName name, Object bean, Locale locale){
          JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
          if (bi.isDynamic()){
              DynamicPropertyHandler handler = PropertyAccessHelper.getDynamicPropertyHandler(bi.getDynamicPropertyHandlerClass());
              return new DynamicPointer(name, bean, handler, locale);
          }
          return null;
      }
  
      public NodePointer createNodePointer(NodePointer parent, QName name, Object bean){
          JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
          if (bi.isDynamic()){
              DynamicPropertyHandler handler = PropertyAccessHelper.getDynamicPropertyHandler(bi.getDynamicPropertyHandlerClass());
              return new DynamicPointer(parent, name, bean, handler);
          }
          return null;
      }
  }
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointerFactory.java
  
  Index: NodePointerFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NodePointerFactory.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.ri.compiler.QName;
  import java.util.*;
  
  /**
   * Creates NodePointers for objects of a certain type.
   * NodePointerFactories are ordered according to the values returned
   * by the "getOrder" method and always queried in that order.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public interface NodePointerFactory {
  
      /**
       * The factory name determines its position between other factories.
       */
      public int getOrder();
  
      /**
       * Create a NodePointer for the supplied object.  The node will represent
       * the "root" object a path.
       *
       * Return null if this factory does not recognize objects of the supplied type.
       */
      public NodePointer createNodePointer(QName name, Object object, Locale locale);
  
      /**
       * Create a NodePointer for the supplied child object.
       * <p>
       * Return null if this factory does not recognize objects of the supplied type.
       */
      public NodePointer createNodePointer(NodePointer parent, QName name, Object object);
  }
  
  
  1.1                  jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullElementPointer.java
  
  Index: NullElementPointer.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/pointers/NullElementPointer.java,v 1.1 2002/04/10 03:40:20 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:20 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.jxpath.ri.pointers;
  
  import org.apache.commons.jxpath.*;
  import org.apache.commons.jxpath.ri.Compiler;
  import org.apache.commons.jxpath.ri.compiler.*;
  
  import java.lang.reflect.*;
  import java.util.*;
  import java.beans.*;
  
  /**
   * Used when there is a need to construct a Pointer for
   * a collection element that does not exist.  For example,
   * if the path is "foo[3]", but the collection "foo" only has
   * one element or is empty or is null, the NullElementPointer
   * can be used to capture this situatuin without putting
   * a regular NodePointer into an invalid state.  Just create
   * a NullElementPointer with index 2 (= 3 - 1) and a "foo" pointer
   * as the parent.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:20 $
   */
  public class NullElementPointer extends PropertyOwnerPointer {
  
      public NullElementPointer(NodePointer parent, int index){
          super(parent);
          this.index = index;
      }
  
      public QName getName(){
          return null;
      }
  
      public Object getBaseValue(){
          return null;
      }
  
      public void setValue(Object value){
          throw new UnsupportedOperationException("Cannot setValue of an object that is not some other object's property");
      }
  
      public boolean isActual(){
          return false;
      }
  
      public void createPath(JXPathContext context, Object value){
          parent.createPath(context, index, value);
      }
  
      public NodePointer createPath(JXPathContext context){
          return parent.createPath(context, index);
      }
  
      public int hashCode(){
          return getParent().hashCode() + index;
      }
  
      public boolean equals(Object object){
          if (object == this){
              return true;
          }
  
          if (!(object instanceof NullElementPointer)){
              return false;
          }
  
          NullElementPointer other = (NullElementPointer)object;
          return getParent() == other.getParent() &&
              index == other.index;
      }
  
      public String asPath(){
          return parent.asPath() + "[" + (index + 1) + "]";
      }
  
      public int getLength(){
          return 0;
      }
  }
  
  
  1.10      +158 -21   jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/JXPathTestCase.java
  
  Index: JXPathTestCase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/JXPathTestCase.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- JXPathTestCase.java	26 Sep 2001 23:37:38 -0000	1.9
  +++ JXPathTestCase.java	10 Apr 2002 03:40:21 -0000	1.10
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/JXPathTestCase.java,v 1.9 2001/09/26 23:37:38 dmitri Exp $
  - * $Revision: 1.9 $
  - * $Date: 2001/09/26 23:37:38 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/JXPathTestCase.java,v 1.10 2002/04/10 03:40:21 dmitri Exp $
  + * $Revision: 1.10 $
  + * $Date: 2002/04/10 03:40:21 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -93,7 +93,7 @@
    * </p>
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.9 $ $Date: 2001/09/26 23:37:38 $
  + * @version $Revision: 1.10 $ $Date: 2002/04/10 03:40:21 $
    */
   
   public class JXPathTestCase extends TestCase
  @@ -313,7 +313,7 @@
               testGetValue(context, "$z/int",  new Integer(1));
               testGetValue(context, "$z/integers[$x - 5]",  new Integer(2));
               testGetValue(context, ".",  bean.getBeans());
  -            testGetValue(context, ".[2]/name",  "Name 2");
  +//            testGetValue(context, ".[2]/name",  "Name 2");        // TBD: is this even legal?
               testGetValue(context, "$t[2]",  "b");
               testGetValue(context, "$m/Key1",  "Value 1");
           }
  @@ -321,7 +321,6 @@
   
       private void testGetValue(JXPathContext context, String xpath, Object expected) {
           Object actual = context.getValue(xpath);
  -//        System.err.println("xpath: " + xpath + " ACTUAL: " + actual);
           assertEquals("Evaluating <" + xpath + ">", expected, actual);
       }
   
  @@ -356,7 +355,7 @@
               testContextDependency("/foo[$x]", false);
               testContextDependency("/foo[bar]", true);
               testContextDependency("3 + 5", false);
  -            testContextDependency("test:func(3, 5)", false);
  +            testContextDependency("test:func(3, 5)", true);
               testContextDependency("test:func(3, foo)", true);
           }
       }
  @@ -390,9 +389,15 @@
               context.setValue("int", new Integer(3));
               assertEquals("Modified <" + "int" + ">", new Integer(3), context.getValue("int"));
   
  +            context.setValue("int", new int[]{4});
  +            assertEquals("Modified <" + "int" + ">", new Integer(4), context.getValue("int"));
  +
               context.setValue("integers[2]", new Integer(5));
               assertEquals("Modified <" + "integers[2]" + ">", new Integer(5), context.getValue("integers[2]"));
   
  +            context.setValue("integers[2]", new int[]{6});
  +            assertEquals("Modified <" + "integers[2]" + ">", new Integer(6), context.getValue("integers[2]"));
  +
               NestedTestBean nBean = new NestedTestBean("Name 9");
               tBean.getBeans()[1] = null;
               context.setValue("beans[2]", nBean);
  @@ -410,6 +415,83 @@
           }
       }
   
  +    /**
  +     * Test JXPath.createPath() with various arguments
  +     */
  +    public void testCreatePath(){
  +        if (enabled){
  +            TestBean tBean = new TestBean();
  +            tBean.setNestedBean(null);
  +            tBean.setBeans(null);
  +            tBean.setMap(null);
  +            JXPathContext context = JXPathContext.newContext(tBean);
  +            context.setFactory(new TestFactory());
  +
  +            // Calls factory.declareVariable("string")
  +            testCreatePath(context, "$string", "Value");
  +
  +            // Calls factory.declareVariable("stringArray"). The factory needs to create a collection
  +            testCreatePath(context, "$stringArray[2]", "Value2");
  +            assertEquals("Created <" + "$stringArray[1]" + ">", "Value1", context.getValue("$stringArray[1]"));
  +
  +            context.getVariables().declareVariable("array", new String[]{"Value1"});
  +
  +            // Does not involve factory at all - just expands the collection
  +            testCreatePath(context, "$array[2]", "Value2");
  +            assertEquals("Created <" + "$array[1]" + ">", "Value1", context.getValue("$array[1]"));
  +
  +            // Calls factory.declareVariable("test"). The factory should create a TestBean
  +            testCreatePath(context, "$test/boolean", Boolean.TRUE);
  +
  +            // Calls factory.declareVariable("testArray").
  +            // The factory should create a collection of TestBeans.
  +            // Then calls factory.createObject(..., collection, "testArray", 1).
  +            // That one should produce an instance of TestBean and put it in the collection
  +            // at index 1.
  +            testCreatePath(context, "$testArray[2]/boolean", Boolean.TRUE);
  +
  +            // Calls factory.createObject(..., TestBean, "nestedBean")
  +            testCreatePath(context, "nestedBean/int", new Integer(1));
  +
  +            // Calls factory.expandCollection(..., testBean, "beans", 2), then
  +            // factory.createObject(..., testBean, "beans", 2)
  +            testCreatePath(context, "beans[2]/int", new Integer(2));
  +
  +            // Another, but the collection already exists
  +            testCreatePath(context, "beans[3]/int", new Integer(3));
  +
  +            // Calls factory.expandCollection(..., testBean, "beans", 2), then
  +            // sets the value
  +            testCreatePath(context, "nestedBean/strings[2]", "Test");
  +
  +            // Calls factory.createObject(..., testBean, "map"), then
  +            // sets the value
  +            testCreatePath(context, "map[@name = 'TestKey1']", "Test");
  +
  +            // Calls factory.createObject(..., testBean, "map"), then
  +            // then factory.createObject(..., map, "TestKey2"), then
  +            // sets the value
  +            testCreatePath(context, "map[@name = 'TestKey2']/int", new Integer(4));
  +
  +            // Calls factory.expandCollection(..., map, "TestKey3", 2), then
  +            testCreatePath(context, "map/TestKey3[2]", "Test");
  +
  +            // Should be the same as the one before
  +            testCreatePath(context, "map[@name='TestKey3'][3]", "Test");
  +
  +            // Comprehensive tests: map & bean
  +            tBean.setMap(null);
  +            testCreatePath(context, "map[@name = 'TestKey5']/nestedBean/int", new Integer(5));
  +            tBean.setMap(null);
  +            testCreatePath(context, "map[@name = 'TestKey5']/beans[2]/int", new Integer(6));
  +        }
  +    }
  +
  +    private void testCreatePath(JXPathContext context, String path, Object value){
  +        context.createPath(path, value);
  +        assertEquals("Created <" + path + ">", value, context.getValue(path));
  +    }
  +
       public void testNull(){
           if (enabled){
               JXPathContext context = JXPathContext.newContext(new TestNull());
  @@ -436,6 +518,30 @@
           }
       }
   
  +    private static class Context implements ExpressionContext {
  +        private Object object;
  +
  +        public Context(Object object){
  +            this.object = object;
  +        }
  +
  +        public Pointer getContextNodePointer(){
  +            return NodePointer.createNodePointer(null, object, Locale.getDefault());
  +        }
  +
  +        public List getContextNodeList(){
  +            return null;
  +        }
  +
  +        public JXPathContext getJXPathContext(){
  +            return null;
  +        }
  +
  +        public int getPosition(){
  +            return 0;
  +        }
  +    }
  +
       public void testFunctions(){
           if (enabled){
               Object[] args;
  @@ -446,19 +552,35 @@
   
               args = new Object[]{new Integer(1), "x"};
               func = funcs.getFunction("test", "new", args);
  -            assertEquals("test:new(1, x)", func.invoke(args).toString(), "foo=1; bar=x");
  +            assertEquals("test:new(1, x)", func.invoke(new Context(null), args).toString(), "foo=1; bar=x");
  +
  +            args = new Object[]{"baz"};
  +            func = funcs.getFunction("test", "new", args);
  +            assertEquals("test:new('baz')", func.invoke(new Context(new Integer(1)), args).toString(), "foo=1; bar=baz");
   
               args = new Object[]{new Integer(1), "x"};
               func = funcs.getFunction("test", "build", args);
  -            assertEquals("test:build(1, x)", func.invoke(args).toString(), "foo=1; bar=x");
  +            assertEquals("test:build(1, x)", func.invoke(new Context(null), args).toString(), "foo=1; bar=x");
   
               args = new Object[]{"7", new Integer(1)};
               func = funcs.getFunction("test", "build", args);
  -            assertEquals("test:build('7', 1)", func.invoke(args).toString(), "foo=7; bar=1");
  +            assertEquals("test:build('7', 1)", func.invoke(new Context(null), args).toString(), "foo=7; bar=1");
   
               args = new Object[]{test};
               func = funcs.getFunction("test", "getFoo", args);
  -            assertEquals("test:getFoo($test, 1, x)", func.invoke(args).toString(), "0");
  +            assertEquals("test:getFoo($test, 1, x)", func.invoke(new Context(null), args).toString(), "0");
  +
  +            args = new Object[0];
  +            func = funcs.getFunction("test", "path", args);
  +            assertEquals("test:path()", func.invoke(new Context(new Integer(1)), args), "1");
  +
  +            args = new Object[]{test};
  +            func = funcs.getFunction("test", "instancePath", args);
  +            assertEquals("test:instancePath()", func.invoke(new Context(new Integer(1)), args), "1");
  +
  +            args = new Object[]{test, "*"};
  +            func = funcs.getFunction("test", "pathWithSuffix", args);
  +            assertEquals("test:pathWithSuffix('*')", func.invoke(new Context(new Integer(1)), args), "1*");
           }
       }
   
  @@ -533,7 +655,6 @@
               try {
                   if (!xpath_tests[i].path && !xpath_tests[i].eval){
                       Pointer ptr = ctx.locateValue(xpath_tests[i].xpath);
  -//                  System.err.println(xpath_tests[i].xpath + " ptr: " + ptr.getClass() + "\n  " + ptr.asPath());
                       Pointer test = ctx.locateValue(ptr.asPath());
                       assertEquals("Testing pointer for <" + xpath_tests[i].xpath + ">", ptr.asPath(), test.asPath());
                   }
  @@ -627,6 +748,7 @@
           // child::
           test("count(set)", new Double(3)),
           test("boolean", Boolean.FALSE),
  +//        test("boolean/class/name", "java.lang.Boolean"),
           testEval("foo:boolean", list()),
           test("count(@*)", new Double(0)),
           testPath("boolean", "/boolean"),
  @@ -657,11 +779,12 @@
           // descendant-or-self::
           testEval("//name", list("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4")),
           test("//Key1", "Value 1"),
  +
           testEval("//self::node()[name = 'Name 0']/name", list("Name 0")),
           testEval("//self::node()[name(.) = concat('n', 'a', 'm', 'e')]",
                   list("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4")),
  -        test("count(//self::beans)", new Double(4)),
  -        test("count(nestedBean//.)", new Double(13)),
  +        test("count(//self::beans)", new Double(2)),
  +        test("count(nestedBean//.)", new Double(7)),
           testEval("descendant-or-self::name", list("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4")),
           test("count(descendant-or-self::root)", new Double(1)),
           test("count(descendant-or-self::node())", new Double(66)),
  @@ -797,7 +920,10 @@
           test("string(test:increment(8))", "9.0"),
           test("length('foo')", new Integer(3)),
           test("call:substring('foo', 1, 2)", "o"),
  +        test("//.[test:isMap()]/Key1", "Value 1"),
  +        test("count(//.[test:count(strings) = 3])", new Double(7)),
   
  +        test("/beans[contains(test:path(), '[2]')]/name", "Name 2"),
   
           // null
           testPath("$testnull/nothing", "$testnull/nothing"),
  @@ -872,9 +998,9 @@
       }
   
       public void testDOM(){
  -//        if (!enabled){
  -//            return;
  -//        }
  +        if (!enabled){
  +            return;
  +        }
           System.setProperty(JXPathContextFactory.FACTORY_NAME_PROPERTY,
                   "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl");
           try {
  @@ -884,9 +1010,8 @@
               ctx.setLocale(Locale.US);
               ctx.getVariables().declareVariable("dom", doc);
               ctx.getVariables().declareVariable("object", docCtr);
  -            TestBeanWithDOM tbwdom = new TestBeanWithDOM();
  -            tbwdom.setVendor(doc.getDocumentElement());
  -            tbwdom.setObject(docCtr);
  +            ctx.getVariables().declareVariable("null", null);
  +            TestBeanWithDOM tbwdom = createTestBeanWithDOM();
               ctx.getVariables().declareVariable("test", tbwdom);
               testXPaths(ctx, dom_tests, false);
           }
  @@ -896,6 +1021,15 @@
           }
       }
   
  +    private TestBeanWithDOM createTestBeanWithDOM(){
  +        XMLDocumentContainer docCtr = new XMLDocumentContainer(getClass().getResource("Test.properties"));
  +        Document doc = (Document)docCtr.getValue();
  +        TestBeanWithDOM tbwdom = new TestBeanWithDOM();
  +        tbwdom.setVendor(doc.getDocumentElement());
  +        tbwdom.setObject(docCtr);
  +        return tbwdom;
  +    }
  +
       static final XP[] dom_tests = new XP[]{
           test("vendor/location/address/street", "Some street"),
           test("vendor/location[2]/address/street", "Other street"),
  @@ -946,7 +1080,7 @@
           testLenient("//foo:x/self::x/y", null),
   
           test("//nsnode/comment()", "z"),
  -        test("//nsnode/text()", "text"),
  +//        test("//nsnode/text()", "text"),
           testPath("//nsnode/text()", "/vendor[1]/nsnode[1]/text()[1]"),
           test("//nsnode/processing-instruction()", "ahead"),
           test("//nsnode/processing-instruction('do')", "it"),
  @@ -968,6 +1102,9 @@
           testPath("$test/object/vendor/location[1]//street", "$test/object/vendor[1]/location[1]/address[1]/street[1]"),
           test("$object//street", "Some street"),
           testPath("$object//street", "$object/vendor[1]/location[1]/address[1]/street[1]"),
  +
  +        testPath("$null", "$null"),
  +//        testPath("$null[3]", "$null[3]"),
       };
   }
   
  
  
  
  1.2       +13 -4     jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/NestedTestBean.java
  
  Index: NestedTestBean.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/NestedTestBean.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- NestedTestBean.java	23 Aug 2001 00:47:02 -0000	1.1
  +++ NestedTestBean.java	10 Apr 2002 03:40:21 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/NestedTestBean.java,v 1.1 2001/08/23 00:47:02 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:47:02 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/NestedTestBean.java,v 1.2 2002/04/10 03:40:21 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:21 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -69,10 +69,11 @@
    * A general purpose JavaBean for JUnit tests for the "jxpath" component.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:47:02 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:21 $
    */
   public class NestedTestBean {
       private String name = "Name 0";
  +    private int integer = 1;
   
       public NestedTestBean(){
       }
  @@ -92,7 +93,11 @@
        * A read-only int property
        */
       public int getInt(){
  -        return 1;
  +        return integer;
  +    }
  +
  +    public void setInt(int value){
  +        this.integer = value;
       }
   
       /**
  @@ -106,6 +111,10 @@
   
       public String[] getStrings(){
           return strings;
  +    }
  +
  +    public void setStrings(String[] array){
  +        strings = array;
       }
   
       public String toString(){
  
  
  
  1.2       +27 -14    jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestBean.java
  
  Index: TestBean.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestBean.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestBean.java	23 Aug 2001 00:47:02 -0000	1.1
  +++ TestBean.java	10 Apr 2002 03:40:21 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestBean.java,v 1.1 2001/08/23 00:47:02 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:47:02 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestBean.java,v 1.2 2002/04/10 03:40:21 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:21 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -68,7 +68,7 @@
    * General purpose test bean for JUnit tests for the "jxpath" component.
    *
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:47:02 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:21 $
    */
   public class TestBean {
   
  @@ -79,15 +79,20 @@
        * An array of nested java beans.
        */
       private NestedTestBean[] beans;
  +    {
  +        beans = new NestedTestBean[2];
  +        beans[0] = new NestedTestBean("Name 1");
  +        beans[1] = new NestedTestBean("Name 2");
  +    }
  +
       public NestedTestBean[] getBeans(){
  -        if (beans == null){
  -            beans = new NestedTestBean[2];
  -            beans[0] = new NestedTestBean("Name 1");
  -            beans[1] = new NestedTestBean("Name 2");
  -        }
           return beans;
       }
   
  +    public void setBeans(NestedTestBean[] beans){
  +        this.beans = beans;
  +    }
  +
       /**
        * A boolean property.
        */
  @@ -146,22 +151,30 @@
        * A Map
        */
       private HashMap map;
  +    {
  +        map = new HashMap();
  +        map.put("Key1", "Value 1");
  +        map.put("Key2", new NestedTestBean("Name 6"));
  +    }
  +
       public Map getMap(){
  -        if (map == null){
  -            map = new HashMap();
  -            map.put("Key1", "Value 1");
  -            map.put("Key2", new NestedTestBean("Name 6"));
  -//            map.put("Key3", null);
  -        }
           return map;
       }
   
  +    public void setMap(HashMap map){
  +        this.map = map;
  +    }
  +
       /**
        * A nested read-only java bean
        */
       private NestedTestBean nestedBean = new NestedTestBean("Name 0");
       public NestedTestBean getNestedBean(){
           return nestedBean;
  +    }
  +
  +    public void setNestedBean(NestedTestBean bean){
  +        this.nestedBean = bean;
       }
   
       private NestedTestBean object = new NestedTestBean("Name 5");
  
  
  
  1.2       +40 -3     jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFunctions.java
  
  Index: TestFunctions.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFunctions.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestFunctions.java	23 Aug 2001 00:47:02 -0000	1.1
  +++ TestFunctions.java	10 Apr 2002 03:40:21 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFunctions.java,v 1.1 2001/08/23 00:47:02 dmitri Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/23 00:47:02 $
  + * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFunctions.java,v 1.2 2002/04/10 03:40:21 dmitri Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/10 03:40:21 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -65,7 +65,7 @@
   
   /**
    * @author Dmitri Plotnikov
  - * @version $Revision: 1.1 $ $Date: 2001/08/23 00:47:02 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/10 03:40:21 $
    */
   public class TestFunctions {
   
  @@ -80,6 +80,11 @@
           this.bar = bar;
       }
   
  +    public TestFunctions(ExpressionContext context, String bar){
  +        this.foo = ((Number)context.getContextNodePointer().getValue()).intValue();
  +        this.bar = bar;
  +    }
  +
       public int getFoo(){
           return foo;
       }
  @@ -103,5 +108,37 @@
   
       public String toString(){
           return "foo=" + foo + "; bar=" + bar;
  +    }
  +
  +    public static String path(ExpressionContext context){
  +        return context.getContextNodePointer().asPath();
  +    }
  +
  +    public String instancePath(ExpressionContext context){
  +        return context.getContextNodePointer().asPath();
  +    }
  +
  +    public String pathWithSuffix(ExpressionContext context, String suffix){
  +        return context.getContextNodePointer().asPath() + suffix;
  +    }
  +
  +    public String className(ExpressionContext context, ExpressionContext child){
  +        return context.getContextNodePointer().asPath();
  +    }
  +
  +    /**
  +     * Returns true if the current node in the current context is a map
  +     */
  +    public static boolean isMap(ExpressionContext context){
  +        Pointer ptr = context.getContextNodePointer();
  +        return ptr == null ? false : (ptr.getValue() instanceof Map);
  +    }
  +
  +    /**
  +     * Returns the number of nodes in the context that is passed as
  +     * the first argument.
  +     */
  +    public static int count(ExpressionContext context, Collection col){
  +        return col.size();
       }
   }
  
  
  
  1.1                  jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFactory.java
  
  Index: TestFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/jxpath/src/test/org/apache/commons/jxpath/TestFactory.java,v 1.1 2002/04/10 03:40:21 dmitri Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/10 03:40:21 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.commons.jxpath;
  
  import java.util.*;
  
  /**
   * Test AbstractFactory.
   *
   * @author Dmitri Plotnikov
   * @version $Revision: 1.1 $ $Date: 2002/04/10 03:40:21 $
   */
  public class TestFactory extends AbstractFactory {
  
      /**
       * Create a new instance and put it in the collection on the parent object.
       * Return the created object or <b>null</b> if this factory cannot create
       * the requested object.
       */
      public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index){
          if (name.equals("testArray")){
              ((TestBean[])parent)[index] = new TestBean();
              return true;
          }
          else if (name.equals("nestedBean")){
              ((TestBean)parent).setNestedBean(new NestedTestBean("newName"));
              return true;
          }
          else if (name.equals("beans")){
              ((TestBean)parent).getBeans()[index] = new NestedTestBean("newName");
              return true;
          }
          else if (name.equals("map")){
              ((TestBean)parent).setMap(new HashMap());
              return true;
          }
          else if (name.equals("TestKey2")){
              ((Map)parent).put(name, new NestedTestBean("newName"));
              return true;
          }
          else if (name.equals("TestKey3")){
              ((Map)parent).put(name, new Vector());
              return true;
          }
          else if (name.equals("TestKey5")){
              TestBean tb = new TestBean();
              tb.setNestedBean(null);
              tb.setBeans(null);
              ((Map)parent).put(name, tb);
              return true;
          }
          return false;
      }
  
      /**
       * Create a new object and set it on the specified variable
       */
      public boolean declareVariable(JXPathContext context, String name){
          if (name.equals("test")){
              context.getVariables().declareVariable(name, new TestBean());
              return true;
          }
          else if (name.equals("testArray")){
              context.getVariables().declareVariable(name, new TestBean[0]);
              return true;
          }
          else if (name.equals("stringArray")){
              context.getVariables().declareVariable(name, new String[]{"Value1"});
              return true;
          }
          context.getVariables().declareVariable(name, null);
          return true;
      }
  
      public boolean expandCollection(JXPathContext context, Pointer pointer, Object parent, String name, int size){
          if (name.equals("beans")){
              TestBean bean = (TestBean)parent;
              bean.setBeans(new NestedTestBean[size]);
              return true;
          }
          else if (name.equals("strings")){
              NestedTestBean bean = (NestedTestBean)parent;
              bean.setStrings(new String[size]);
              return true;
          }
          return false;
      }
  }
  
  
  
  1.1                  jakarta-commons/jxpath/xdocs/contributors.xml
  
  Index: contributors.xml
  ===================================================================
  <?xml version="1.0"?>
  <document>
     <properties>
        <title>Contributors</title>
        <author email="commons-dev@jakarta.apache.org">Commons Documentation Team</author>
        <author email="dmitri@apache.org">Dmitri Plotnikov</author>
        <revision>$Id: contributors.xml,v 1.1 2002/04/10 03:40:21 dmitri Exp $</revision>
     </properties>
  
     <body>
        <section name="Contributors">
           <p>
              The original code of <em>JXPath</em> was developed by Dmitri Plotnikov of PLOTNIX, Inc and
              contributed to Jakarta.
           </p>
           <p>
              After the original contribution, the following volunteers have contributed code,
              documentation, testing and support to <em>JXPath</em>.
           </p>
           <!-- alphabetical by last name please -->
           <ul>
              <li>Craig R. McClanahan</li>
              <li>Dmitri Plotnikov</li>
           </ul>
           <p>
              Special thanks to <i>James Strachan</i>, whose help in
              bringing JXPath to Apache cannot be overestimated.
           </p>
        </section>
     </body>
  </document>
  
  
  
  1.1                  jakarta-commons/jxpath/xdocs/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0"?>
  <document>
     <properties>
        <title>Home</title>
        <author email="commons-dev@jakarta.apache.org">Commons Documentation Team</author>
        <author email="dmitri@plotnix.com">Dmitri Plotnikov</author>
        <revision>$Id: index.xml,v 1.1 2002/04/10 03:40:21 dmitri Exp $</revision>
     </properties>
  
     <body>
        <section name="The JXPath Component">
            <p>
               The <code>org.apache.commons.jxpath</code> package defines a simple
               interpreter of an expression language called XPath. JXPath applies <em>XPath</em>
               expressions to graphs of objects of all kinds: JavaBeans, Maps, Servlet contexts, DOM etc,
               including mixtures thereof.
            </p>
            <p>
               Consider this example:
            </p>
                <blockquote><pre>
  Address address = (Address)JXPath.getValue(vendor,
           "locations[address/zipCode='90210']/address");
                </pre></blockquote>
            <p>
              This XPath expression is equvalent to the following Java code:
            </p>
  <blockquote><pre>
  Address address = null;
  Collection locations = vendor.getLocations();
  Iterator it = locations.iterator();
  while (it.hasNext()){
      Location location = (Location)it.next();
      String zipCode = location.getAddress().getZipCode();
      if (zipCode.equals("90210")){
        address = location.getAddress();
        break;
      }
  }
  </pre></blockquote>
            <p>
              XPath was standardized by W3C and is used in both XSLT and XPointer.
            </p>
            <p>
              If you want to find out more about XPath, a good place to start is
              is an excellent XPath Tutorial by <a href="http://www.w3schools.com/xpath">W3Schools</a>
            </p>
            <p>
              The official definition of XPath by W3C can be found at
              <a href="http://www.w3.org/TR/xpath">XML Path Language (XPath) Version 1.0</a>
            </p>
            <p>
              Primary applications of JXPath are in scripting: JSP and similar template/script based technologies.
              However, programmers who like a more XML-flavored APIs, should consider JXPath as
              an alternative to other expression languages as well. JXPath is a must-have tool
              for those who work with mixtures of Java objects and XML and need to frequently
              traverse through graphs of those.
            </p>
            <p>
              JXPath documentation currently contains:
              <ul>
                <li><a href="users-guide.html">User's Guide</a></li>
                <li><a href="api/index.html">JavaDoc API Documentation</a></li>
  <!--              <li><a href="design.html">JXPath Design</a>, which is a document
                  primarily intended for those who are interested in extending JXPath.
                  It could also be quite useful if you are debugging a tough problem.
                </li>
  -->
              </ul>
            </p>
        </section>
     </body>
  </document>
  
  
  
  1.1                  jakarta-commons/jxpath/xdocs/users-guide.xml
  
  Index: users-guide.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <document>
  
   <properties>
    <title>JXPath User's Guide</title>
    <author email="dmitri@apache.org">Dmitri Plotnikov</author>
   </properties>
  
   <body>
  
  <section name="What's JXPath">
  
      <p>
       JXPath provides APIs for the traversal of graphs of JavaBeans, DOM and
       other types of objects using the XPath syntax.
      </p>
  
      <p>
       If you are not familiar with the XPath syntax, start with
       <a href="http://www.w3schools.com/xpath">XPath Tutorial by W3Schools</a>.<br/>
       Also see <a href="http://www.w3.org/TR/xpath">XML Path Language (XPath) Version 1.0 </a>
      </p>
  
      <p>
       You can read and write properties
       of JavaBeans, get and set elements of arrays, collections,
       maps, DOM documents, transparent containers, various context objects
       in Servlets etc.
      </p>
  
      <p>
       You can also have JXPath create new objects if needed.
      </p>
  
      <p>
       The central class in the JXPath architecture is
       <a href="api/org/apache/commons/jxpath/JXPathContext.html"><code>JXPathContext</code></a>. Most of the
       APIs discussed in this document have to do with the JXPathContext class.
      </p>
  </section>
  
  <section name="Object Graph Traversal">
      <p>
       JXPath uses JavaBeans introspection to enumerate and access JavaBeans
       properties.
      </p>
      <p>
       The interpretation of the xpath syntax in the context of Java object graphs
       is quite intuitive: the "child" axis of XPath is mapped to JavaBean properties.
      </p>
  
  <subsection name="JavaBean Property Access">
   <p>
      JXPath can be used to access properties of a JavaBean.
   </p>
  <source>
                                                                         &#xA0;
  public class Employee {
      public String getFirstName(){
         ...
      }
  }
  
  Employee emp = new Employee();
  ...
  
  JXPathContext context = JXPathContext.newContext(emp);
  String fName = (String)context.getValue("firstName");
  </source>
  
  <p>
       In this example, we are using JXPath to access a property of the <code>emp</code> bean.
       In this simple case the invocation of JXPath is equivalent to invocation of getFirstName()
       on the bean.
  </p>
  </subsection>
  
  
  <subsection name="Nested Bean Property Access">
   <p>
   JXPath can traverse object graphs:
   </p>
   <source>
                                                                         &#xA0;
   public class Employee {
      public Address getHomeAddress(){
         ...
      }
   }
   public class Address {
      public String getStreetNumber(){
         ...
      }
   }
  
   Employee emp = new Employee();
   ...
  
   JXPathContext context = JXPathContext.newContext(emp);
   String sNumber = (String)context.getValue("homeAddress/streetNumber");
   </source>
   <p>
   In this case XPath is used to access a property of a nested bean.
   </p>
   <p>
   A property identified by the xpath does not have to be a "leaf" property.
   For instance, we can extract the whole Address object in above example:
   </p>
   <source>
                                                                         &#xA0;
      Address addr = (Address)context.getValue("homeAddress");
   </source>
   </subsection>
  
  <subsection name="Collection Subscripts">
   <p>
    JXPath can extract elements from arrays and collections.
   </p>
   <source>
                                                                         &#xA0;
   public class Integers {
      public int[] getNumbers(){
         ...
      }
   }
  
   Integers ints = new Integers();
   ...
  
   JXPathContext context = JXPathContext.newContext(ints);
   Integer thirdInt = (Integer)context.getValue("numbers[3]");
   </source>
   <p>
    A collection can be an arbitrary array or an instance of java.util.Collection.
   </p>
   <p>
    <b>Note:</b> in XPath the first element of a collection has index 1, not 0.<br/>
   </p>
  </subsection>
  
  
  <subsection name="Map Element Access">
   <p>
    JXPath supports maps. To get a value use its key.
   </p>
   <source>
                                                                         &#xA0;
   public class Employee {
      public Map getAddresses(){
         return addressMap;
      }
  
      public void addAddress(String key, Address address){
         addressMap.put(key, address);
      }
      ...
   }
  
   Employee emp = new Employee();
   emp.addAddress("home", new Address(...));
   emp.addAddress("office", new Address(...));
   ...
  
   JXPathContext context = JXPathContext.newContext(emp);
   String homeZipCode =
          (String)context.getValue("addresses/home/zipCode");
   </source>
  
   <p>
   Often you will need to use the alternative syntax for accessing Map
   elements:
   </p>
   <source>
                                                                         &#xA0;
   String homeZipCode = (String)context.
          getValue("addresses[@name='home']/zipCode");
   </source>
  <p>
   The attribute "name" is not reserved for the use with Maps. It can represent
   a property name of a JavaBean as well. Unlike a child name in XPath,
   the value of the "name" attribute does <em>not</em> have to be a properly formed
   identifier.  Also, in this case the key can be an expression, e.g. a variable.
  </p>
  
  <p>
   <b>Note:</b> At this point JXPath only supports Maps that use strings for keys.
  </p>
  <p>
   <b>Note:</b> JXPath supports the extended notion of Map: any object with
     dynamic properties can be handled by JXPath provided that its
     class is registered with the
     <a href="api/org/apache/commons/jxpath/JXPathIntrospector.html"><code>JXPathIntrospector</code></a>.
  </p>
  </subsection>
  
  <subsection name="Retrieving Multiple Results">
   <p>
   JXPath can retrieve multiple objects from a graph. Note that the method
   called in this case is not <code>getValue</code>, but <code>eval</code>.
   </p>
   <source>
                                                                         &#xA0;
   public class Author {
      public Book[] getBooks(){
         ...
      }
   }
  
   Author auth = new Author();
   ...
  
   JXPathContext context = JXPathContext.newContext(auth);
   List threeBooks = (List)context.eval("books[position() &lt; 4]");
   </source>
   <p>
   This returns a list of at most three books from the array of all books
   written by the author.
   </p>
  </subsection>
  
  <subsection name="DOM Document Access">
  <p>
  JXPath supports access to DOM Nodes. The DOM node can be the
  context node of JXPathContext or it can be a value of a property,
  element of a collection,
  variable value etc.  Let's say we have a path
  <code>"$foo/bar/baz"</code>. It will work
  if, for instance, the value of the variable "foo" is a
  JavaBean, whose property "bar" contains a DOM Node,
  which has a child element named "baz".
  </p>
  <p>
  The intepretation of XPath over DOM structures is
  implemented in with the XPath specification. For
  instance, the "attribute" axis is supported for
  DOM objects, even though it is not applicable to JavaBeans.
  </p>
  </subsection>
  
  <subsection name="Lenient Mode">
  <p>
  The <code>jxpathContext.getValue(xpath)</code> method throws an exception if the
  supplied xpath does not map to an existing property/object.  This
  constraint can be relaxed by calling <code>jxpathContext.setLenient(true)</code>.
  In the lenient mode the method merely returns null if the path maps to nothing.
  </p>
  </subsection>
  
  <p><b>Note:</b>
       JXPath does not support DOM attributes for non-DOM objects. Even though XPaths
       like <code>"para[@type='warning']"</code> are legitimate, they will always produce empty results.
       The only attributes supported for JavaBeans are <code>"name"</code> and <code>"xml:lang"</code>.
  </p>
  </section>
  
  <section name="Modifying Object Graphs">
  <p>
  JXPath can also be used to modify parts of object graphs: property values,
  values for keys in Maps.  It can in some cases create intermediate
  nodes in object graphs.
  </p>
  
  <subsection name="Setting Properties">
   <p>
   JXPath can be used to modify property values.
   </p>
   <source>
                                                                         &#xA0;
   public class Employee {
      public Address getAddress() {
         ...
      }
  
      public void setAddress(Address address) {
         ...
      }
   }
  
   Employee emp = new Employee();
   Address addr = new Address();
   ...
  
   JXPathContext context = JXPathContext.newContext(emp);
   context.setValue("address", addr);
   context.setValue("address/zipCode", "90190");
   </source>
  </subsection>
  
  <subsection name="Creating Objects">
  <p>
   JXPath can be used to create new objects. First, create a subclass of
   <a href="api/org/apache/commons/jxpath/AbstractFactory.html"><code>AbstractFactory</code></a>
   and install it on the JXPathContext.
   Then call <code>jxPathContext.createPath(xpath, value)</code> instead of "setValue".
   JXPathContext will invoke your AbstractFactory when it discovers that an
   intermediate node of the path is <b>null</b>.  It will not override existing
   nodes.
  </p>
   <source>
                                                                         &#xA0;
   public class AddressFactory extends AbstractFactory {
      public boolean createObject(JXPathContext context, Pointer pointer,
                                  Object parent, String name, int index){
       if ((parent instanceof Employee) &amp;&amp; name.equals("address"){
         ((Employee)parent).setAddress(new Address());
         return true;
       }
       return false;
     }
   }
  
   JXPathContext context = JXPathContext.newContext(emp);
   context.setFactory(new AddressFactory());
   context.createPath("address/zipCode", "90190");
  </source>
  
  <p>
   Note that it only makes sense to use this functionality with very simple
   paths. In fact, JXPath will not attempt to create intermediate nodes
   for paths that don't follow these two rules:
   <ul>
     <li>The only axis used is "child::", e.g. <code>"foo/bar/baz"</code></li>
     <li>The only two types of predicates used are context-independent
       indexing and the <code>"[@name = <i>expr</i>]"</code> construct, e.g.
       <code>"map[@name='key1'][4/2]"</code>.</li>
     <li>If a variable is used, it is the root of the path, e.g.
       <code>"$object/child"</code>.</li>
   </ul>
  </p>
  </subsection>
  </section>
  
  <section name="Variables">
   <p>
   JXPath supports the notion of variables. The XPath syntax for accessing
   variables is <i>"$varName"</i>.
   </p>
  
   <source>
                                                                         &#xA0;
   public class Author {
      public Book[] getBooks(){
         ...
      }
   }
  
   Author auth = new Author();
   ...
  
   JXPathContext context = JXPathContext.newContext(auth);
   context.getVariables().declareVariable("index", new Integer(2));
  
   Book secondBook = (Book)context.getValue("books[$index]");
   </source>
  
   <p>
   You can also set variables using JXPath:
   </p>
  
   <source>
                                                                         &#xA0;
   context.setValue("$index", new Integer(3));
   </source>
  
   <p>
   <b>Note:</b> generally speaking, you can only <i>change</i> the value of an existing variable this
   way, you cannot <i>define</i> a new variable.  If you do want to define a new variable dynamically,
   implement a <code>defineVariable()</code> method on your custom AbstractFactory and call
   <code>createPath()</code> rather than <code>setValue()</code>.  The restrictions described in
   the "Creating Objects" section still apply.
   </p>
   <p>
   When a variable contains a JavaBean or a collection, you can
   traverse the bean or collection as well:
   </p>
  
   <source>
                                                                         &#xA0;
   ...
   context.getVariables().declareVariable("book", myBook);
   String title = (String)context.getValue("$book/title);
  
   Book array[] = new Book[]{...};
  
   context.getVariables().declareVariable("books", array);
  
   String title = (String)context.getValue("$books[2]/title);
   </source>
  <p>
  
  </p>
  
   <subsection name="Custom Variable Pools">
   <p>
   By default, JXPathContext creates a HashMap of variables. However,
   you can substitute a custom implementation of the Variables
   interface to make JXPath work with an alternative source of variables.
   For example, you can define implementations of Variables that
   cover a servlet context, HTTP request or any similar structure.
   </p>
   <p>See the
   <a href="api/org/apache/commons/jxpath/servlet/package-summary.html">org.apache.commons.jxpath.servlet</a>
   package for an example of just that.
   </p>
   </subsection>
   </section>
  
  <section name="Pointers">
  <p>
  JXPath supports so called Pointers. A Pointer is an
  object that represents a location of a node in an object graph.
  For example, you can call
  </p>
  <source>
                                                                         &#xA0;
  Pointer ptr = context.locateValue("employees[$i]/addresses[$j]")
  </source>
  <p>
  Let's say the value of the variable i is 1 and j = 3.
  If we call <code>ptr.asPath()</code>,
  it returns an XPath that describes this concrete
  location: <code>"/employees[1]/addresses[3]"</code>.
  </p>
  <p>
  If you have an XPath that finds many nodes in the graph, you can
  get JXPath to produce a collection of pointers for all of those
  found locations:
  </p>
  <source>
                                                                         &#xA0;
  List homeAddresses = context.locate("//employee/address[@name='home']");
  </source>
  <p>
  Each Pointer in the list will map to a home address object in the graph.
  </p>
  <p>
  It is a good idea to use pointers whenever you need
  to access the same node of a graph repeatedly.
  </p>
  <p>
  Also, when an XPath is repeatedly evaluated in different contexts (for example,
  with different variable values) and you need to preseve results of those
  evaluations.  A Pointer, as well as the XPath produced by <code>pointer.asPath()</code>
  is context-independent, it won't change if involved variables or expressions change.
  </p>
  <p>
  JXPath is optimized to interpret XPaths produced by Pointers much faster
  than many other types of XPaths.
  </p>
  </section>
  
  <section name="Extension Functions">
  <p>
   JXPath supports standard XPath functions right out of the box.
   It also supports "standard" extension functions, which are basically a bridge
   to Java as well as entirely custom extension functions.
  </p>
  
  <subsection name="Standard Extension Functions">
  <p>
   Using the standard extension functions, you can call methods on objects,
   static methods on classes and create objects using any constructors.
   All class names should be fully qualified.
  </p>
  <p>
   Here's how you can create new objects:
  </p>
  <source>
                                                                         &#xA0;
   Book book = (Book)context.
     getValue("com.myco.books.Book.new('John Updike')");
  </source>
  
  <p>
   Here's how you can call static methods:
  </p>
  <source>
                                                                         &#xA0;
   Book book = (Book)context.
     getValue("com.myco.books.Book.getBestBook('John Updike')");
  </source>
  
  <p>
   Here's how you can call regular methods:
  </p>
   <source>
                                                                         &#xA0;
   String firstName = (String)context.
     getValue("getAuthorsFirstName($book)");
   </source>
  <p>
   As you can see, the target of the method is specified as the first parameter
   of the function.
  </p>
  </subsection>
  
  <subsection name="Custom Extension Functions">
  <p>
   Collections of custom extension functions can be implemented
   as <a href="api/org/apache/commons/jxpath/Functions.html"><code>Functions</code></a>
   objects or as Java classes, whose methods become extenstion functions.
  </p>
  <p>
   Let's say the following class implements various formatting operations:
  </p>
  <source>
                                                                         &#xA0;
   public class Formats {
      public static String date(Date d, String pattern){
          return new SimpleDateFormat(pattern).format(d);
      }
      ...
   }
  </source>
  
  <p>
   We can register this class with a JXPathContext:
  </p>
  <source>
                                                                         &#xA0;
   context.setFunctions(new ClassFunctions(Formats.class, "format"));
   ...
  
   context.getVariables().declareVariable("today", new Date());
   String today =
       (String)context.getValue("format:date($today, 'MM/dd/yyyy')");
  </source>
  <p>
   You can also register whole packages of Java classes using PackageFunctions.
  </p>
  <p>
   Also, see <a href="api/org/apache/commons/jxpath/FunctionLibrary.html"><code>FunctionLibrary</code></a>,
   which is a class that allows you to register multiple sets of extension functions with
   the same JXPathContext.
  </p>
  </subsection>
  
  <subsection name="Expression Context">
  <p>
    A custom function can get access to the context in which it is being
    evaluated.  ClassFunctions and PackageFunctions have special support for
    methods and constructors that have <a href="api/org/apache/commons/jxpath/ExpressionContext.html">
    <code>ExpressionContext</code></a> as the first argument.
    When such extension function is invoked, it is given an object
    that implements the ExpressionContext interface.  The function can then
    gain access to the "current" object in the currently evaluated context.
  </p>
  <source>
                                                                         &#xA0;
  public class MyExtenstionFunctions {
     public static boolean isDate(ExpressionContext context){
        Pointer pointer = context.getContextNodePointer();
        if (pointer == null){
          return false;
        }
        return pointer.getValue() instanceof Date;
     }
     ...
  }
  </source>
  
  <p>
  You can then register this extension function using ClassFunctions and
  call it like this:
  </p>
  <source>
                                                                         &#xA0;
    "//.[myext:isDate()]"
  </source>
  <p>
  This expression will find all nodes of the graph that are dates.
  </p>
  <p>
  The current context is passed to an extension function by default. Any other
  context can be passed as an explicit argument declared as ExpressionContext.
  Note, that if the first argument is ExpressionContext, it is always passed the
  current context. Therefore, you may need to declare two ExpressionContext
  arguments - one for the current and one for the parameter context.
  For example,
  </p>
  <source>
                                                                         &#xA0;
  public class MyExtenstionFunctions {
     ...
     public static boolean contains(ExpressionContext current,
                              ExpressionContext context, Object value){
        Iterator iter = context.getContextNodeList().iterator();
        while (iter.hasNext()) {
            Pointer item = (Pointer)iter.next();
            if (item.getValue().equals(value)){
              return true;
            }
        }
        return false;
     }
  }
  </source>
  <p>
  You can call this function to find all people who have a certain phone number:
  </p>
  <source>
                                                                         &#xA0;
    "/addressBook/contact[myext:contains(phoneNumbers, '555-5555']"
  </source>
  
  </subsection>
  </section>
  
  <section name="Type Conversions">
  <p>
   JXPath automatically performs the following type convertions:
  </p>
  
  <table>
    <tr>
      <th>From type</th>
      <th>To type</th>
      <th>Operation</th>
    </tr>
    <tr><td><i>any</i></td><td>String</td>
          <td>Calls toString()</td></tr>
    <tr><td>Boolean</td><td><i>any</i> Number</td>
          <td>True = 1, false = 0</td></tr>
    <tr><td><i>any</i> Number</td><td><i>any other</i> Number</td>
          <td>Truncates if needed</td></tr>
    <tr><td>String</td><td><i>any primitive type</i></td>
          <td>Parses the string</td></tr>
    <tr><td><i>array of length 1</i></td><td><i>any</i></td>
          <td>Takes the single element of the array <br/>and (recursively) converts it to the needed type</td></tr>
    <tr><td><i>collection of size 1</i></td><td><i>any</i></td>
          <td>Takes the single element of the array <br/>and (recursively) converts it to the needed type</td></tr>
    <tr><td>ExpressionContext</td><td>Collection, List, Vector, Set</td>
          <td>Creates a collection of type ArrayList, <br/>
              ArrayList, Vector, HashSet respectively and <br/>
              populates it with all current context node pointers</td></tr>
    <tr><td>ExpressionContext</td><td><i>any</i></td>
          <td>Obtains value of the current context node pointer <br/>and (recursively) converts it to the needed type</td></tr>
  </table>
  </section>
  
  <section name="Internationalization">
  <p>
  For DOM Documents JXPathContext supports internationalization XPath-style.
  A locale can be declared on an XML Element like this:
  </p>
  <source>
                                                                         &#xA0;
       &lt;book xml:lang="fr"&gt;Les Miserables&lt;/book&gt;
  </source>
  <p>
  You can then use the <code>lang</code> function in XPath to find nodes
  for a specific language:
  </p>
  <source>
                                                                         &#xA0;
       "//book[lang('fr')]
  </source>
  <p>
  The <code>"lang"</code> boolean function is supported for non-DOM objects as well.  It tests
  the Locale set on the JXPathContext (or the default locale).  See
  <a href="api/org/apache/commons/jxpath/JXPathContext.html#setLocale">JXPathContext.setLocale()</a>.
  </p>
  <p>
   You can also utilize the <code>xml:lang</code> attribute, whose value is
   the name of the locale, whether in a DOM document or outside.
  </p>
  </section>
  
  <section name="Containers">
  <p>
   A <a href="api/org/apache/commons/jxpath/Container.html">Container</a> is an
   object implementing an indirection mechanism transparent to JXPath.
  
   For example, if property <code>"foo"</code> of the context node has a Container
   as its value, the XPath "foo" will produce the contents of that Container,
   not the container itself.
  </p>
  <p>
   An example of a useful container is
   <a href="api/org/apache/commons/jxpath/XMLDocumentContainer.html">XMLDocumentContainer</a>.
   When you create an XMLDocumentContainer, you give it a pointer to an XML file
   (a <code>URL</code> or a <code>javax.xml.transform.Source</code>.
   It will read and parse the XML file only when it is
   accessed.  You can create XMLDocumentContainers for various XML documents
   that may or may not be accessed by XPaths.  If they are, they will be automatically
   read, parsed and traversed. If they are not - they won't be read at all.
  </p>
  </section>
  
  <section name="Nested Contexts">
   <p>
   If you need to use the same set of variables while interpreting
   XPaths with different beans, it makes sense to put the variables in a separate
   context and specify that context as a parent context every time you
   allocate a new JXPathContext for a JavaBean.  This way you don't need
   to waste time fully configuring every context.
   </p>
   <p>The same logic
   applies to shared extension functions, abstract factories and locale.
   </p>
  
  <source>
                                                                         &#xA0;
   JXPathContext sharedContext = JXPathContext.newContext(null);
   sharedContext.getVariables().declareVariable("title", "Java");
   sharedContext.setFunctions(new MyExtensionFunctions());
   sharedContext.setLocale(Locale.CANADA);
   sharedContext.setFactory(new MyFactory());
  
   ...
  
   JXPathContext context = JXPathContext.newContext(sharedContext, auth);
  
   List javaBooks = (List)context.
          eval("books[preprocessTitle(title) = $title]");
   </source>
  </section>
  
  
  <section name="Customizing JXPath">
    <p>
    JXPath can be customized on several levels.
    </p>
    <ul>
      <li>You can provide custom JXPathBeanInfo objects to customize lists of
          properties of JavaBeans available to JXPath.</li>
      <li>You can easily add support for object types similar to Map. All you need
          to do is implement the DynamicPropertyHandler interface and register
          the implementation with JXPathIntrospector.</li>
      <li>You can add support for types of object models JXPath does
          not support out of the box. An example of such model would be an alternative
          implementation of XML parse tree (e.g. JDOM etc). You will need to
          implement one or two APIs to allow JXPath to traverse properties of these
          custom objects.</li>
      <li>The most dramatic customization of JXPath can be done at the level of
          JXPathContextFactory - you can transparently provide an alternative
          implementation of all top level APIs.</li>
    </ul>
  
    <subsection name="Custom JXPathBeanInfo">
      <p>
       JXPath uses JavaBeans introspection to discover properties of JavaBeans.
       You can provide alternative property lists by supplying
       custom JXPathBeanInfo classes (see
       <A HREF="api/org/apache/commons/jxpath/JXPathBeanInfo.html"><CODE>JXPathBeanInfo</CODE></A>).
      </p>
    </subsection>
  
    <subsection name="Custom DynamicPropertyHandler">
      <p>
       JXPath uses various implementations of the DynamicPropertyHandler interface
       to access properties of objects similar to Map.
      </p>
      <p>
       The <code>org.apache.commons.jxpath.servlet</code> package has several
       examples of custom DynamicPropertyHandlers.
      </p>
    </subsection>
  
    <subsection name="Custom Pointers and Iterators">
    <p>
      Architecturally, multiple model support is
      made possible by the notions of a
      <a href="api/org/apache/commons/jxpath/ri/pointers/NodePointer.html">NodePointer</a> and
      <a href="api/org/apache/commons/jxpath/ri/pointers/NodeIterator.html">NodeIterator</a>, which are
      simple abstract classes that
      are extended in different ways to traverse graphs
      of objects of different
      kinds.  The NodePointer/NodeIterator APIs are designed with
      models like JavaBeans in mind.  They
      directly support indexed collections.  As a result,
      XPaths like <code>"foo[10]"</code>
      can be executed as <code>"getFoo(9)"</code> or <code>getFoo()[9]</code>,
      or <code>getFoo().get(9)</code>,
      depending on the type of collection.  This flexibility
      is disguised well enough by the APIs of the abstract classes,
      so we can still have a natural implementation of traversal of
      object models, such as DOM, that do not have the same notion of collection.
      </p>
      <p>
      To add support for a new object model, build custom implementations
      of NodePointer and NodeIterator as well as
      <a href="api/org/apache/commons/jxpath/ri/pointers/NodePointerFactory.html">NodePointerFactory</a>.  Then
      register the new factory with
      <a href="api/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.html">JXPathContextReferenceImpl</a>.
      </p>
      <p>
       See existing NodePointerFactories for examples of how that's done:
       <ul>
         <li>BeanPointerFactory works with JavaBeans</li>
         <li>DynamicPointerFactory works with Dynamic beans like Map, HttpRequest and such</li>
         <li>ContainerPointerFactory works with Container objects like XMLDocumentContainer</li>
         <li>DOMPointerFactory works with DOM Nodes</li>
       </ul>
      </p>
    </subsection>
  
    <subsection name="Alternative JXPath Implementation">
      <p>
       JXPathContext allows alternative implementations. This is why instead of
       allocating JXPathContext directly, you should call a static <code>newContext</code>
       method.  This method will utilize the JXPathContextFactory API to locate
       a suitable implementation of JXPath.
       JXPath comes bundled with a default implementation called Reference Implementation.
      </p>
    </subsection>
  </section>
  
  <section name="Miscellaneous">
  <ul>
   <li>The current version of JXPath does not support the <code>id(string)</code>
       and <code>key(key, value)</code> XPath functions.</li>
   </ul>
  </section>
  
  </body>
  </document>
  
  
  1.1                  jakarta-commons/jxpath/xdocs/images/jakarta-logo.gif
  
  	<<Binary file>>
  
  
  1.1                  jakarta-commons/jxpath/xdocs/images/logo.jpg
  
  	<<Binary file>>
  
  
  1.1                  jakarta-commons/jxpath/xdocs/stylesheets/project.xml
  
  Index: project.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <project name="Commons - JXPath" href="http://jakarta.apache.org/commons/jxpath">
      <title>JXPath</title>
      <logo href="/images/logo.jpg">Commons</logo>
      <!--
        Note: Per the .vsl file, "/site" links will be prefixed with "http://jakarta.apache.org/site"
              Other links starting with "/" become "./...".
      -->
      <body>
         <menu name="Home">
             <item name="Jakarta&#xA0;Commons"          href="http://jakarta.apache.org/commons/index.html" />
             <item name="JXPath"                        href="/index.html" />
         </menu>
         <menu name="Information">
  <!--
             <item name="Overview"                      href="/overview.html"/>
             <item name="News"                          href="http://jakarta.apache.org/commons/pool/news.html"/>
  -->
             <item name="Client API&#xA0;Documentation"   href="/api/org/apache/commons/jxpath/package-summary.html"/>
             <item name="Complete API&#xA0;Docs"        href="/api/index.html"/>
             <item name="User's Guide"                  href="/users-guide.html"/>
             <!-- item name="JXPath Design"                 href="/design.html"/ -->
         </menu>
         <menu name="Project Files">
             <item name="Status"                        href="http://cvs.apache.org/viewcvs/~checkout~/jakarta-commons/jxpath/STATUS.html?content-type=text/html"/>
             <item name="CVS"                           href="http://cvs.apache.org/viewcvs/jakarta-commons/jxpath/"/>
             <item name="Original&#xA0;Proposal"        href="http://cvs.apache.org/viewcvs/~checkout~/jakarta-commons/jxpath/PROPOSAL.html?content-type=text/html"/>
         </menu>
         <menu name="Downloads">
             <item name="Nightly Build"                 href="http://jakarta.apache.org/builds/jakarta-commons/nightly/commons-jxpath/"/>
         </menu>
         <menu name="About Us">
             <item name="Contributors"                  href="/contributors.html"/>
             <item name="License"                       href="http://jakarta.apache.org/commons/license.html"/>
         </menu>
         <menu name="Jakarta Community">
             <item name="Get&#xA0;Involved"             href="http://jakarta.apache.org/site/getinvolved.html"/>
             <item name="Mailing&#xA0;Lists"            href="http://jakarta.apache.org/site/mail.html"/>
             <item name="CVS&#xA0;Repositories"         href="http://jakarta.apache.org/site/cvsindex.html"/>
         </menu>
      </body>
  </project>
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>