content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * Copyright 1999-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id: AxesWalker.java,v 1.2.4.1 2005/09/14 19:45:22 jeffsuttor Exp $
   */
  package jaxp.sun.org.apache.xpath.internal.axes;
  
  import java.util.Vector;
  
  import jaxp.sun.org.apache.xalan.internal.res.XSLMessages;
  import jaxp.sun.org.apache.xml.internal.dtm.DTM;
  import jaxp.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
  import jaxp.sun.org.apache.xml.internal.dtm.DTMIterator;
  import jaxp.sun.org.apache.xpath.internal.Expression;
  import jaxp.sun.org.apache.xpath.internal.ExpressionOwner;
  import jaxp.sun.org.apache.xpath.internal.XPathContext;
  import jaxp.sun.org.apache.xpath.internal.XPathVisitor;
  import jaxp.sun.org.apache.xpath.internal.compiler.Compiler;
  import jaxp.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  import jaxp.xml.transform.TransformerException;
  
  /**
   * Serves as common interface for axes Walkers, and stores common
   * state variables.
   */
  public class AxesWalker extends PredicatedNodeTest
          implements Cloneable, PathComponent, ExpressionOwner
  {
      static final long serialVersionUID = -2966031951306601247L;
  
    /**
     * Construct an AxesWalker using a LocPathIterator.
     *
     * @param locPathIterator non-null reference to the parent iterator.
     */
    public AxesWalker(LocPathIterator locPathIterator, int axis)
    {
      super( locPathIterator );
      m_axis = axis;
    }
  
    public final WalkingIterator wi()
    {
      return (WalkingIterator)m_lpi;
    }
  
    /**
     * Initialize an AxesWalker during the parse of the XPath expression.
     *
     * @param compiler The Compiler object that has information about this
     *                 walker in the op map.
     * @param opPos The op code position of this location step.
     * @param stepType  The type of location step.
     *
     * @throws TransformerException
     */
    public void init(Compiler compiler, int opPos, int stepType)
            throws TransformerException
    {
  
      initPredicateInfo(compiler, opPos);
  
      // int testType = compiler.getOp(nodeTestOpPos);
    }
  
    /**
     * Get a cloned AxesWalker.
     *
     * @return A new AxesWalker that can be used without mutating this one.
     *
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException
    {
      // Do not access the location path itterator during this operation!
  
      AxesWalker clone = (AxesWalker) super.clone();
  
      //clone.setCurrentNode(clone.m_root);
  
      // clone.m_isFresh = true;
  
      return clone;
    }
  
    /**
     * Do a deep clone of this walker, including next and previous walkers.
     * If the this AxesWalker is on the clone list, don't clone but
     * return the already cloned version.
     *
     * @param cloneOwner non-null reference to the cloned location path
     *                   iterator to which this clone will be added.
     * @param cloneList non-null vector of sources in odd elements, and the
     *                  corresponding clones in even vectors.
     *
     * @return non-null clone, which may be a new clone, or may be a clone
     *         contained on the cloneList.
     */
    AxesWalker cloneDeep(WalkingIterator cloneOwner, Vector cloneList)
       throws CloneNotSupportedException
    {
      AxesWalker clone = findClone(this, cloneList);
      if(null != clone)
        return clone;
      clone = (AxesWalker)this.clone();
      clone.setLocPathIterator(cloneOwner);
      if(null != cloneList)
      {
        cloneList.addElement(this);
        cloneList.addElement(clone);
      }
  
      if(wi().m_lastUsedWalker == this)
        cloneOwner.m_lastUsedWalker = clone;
  
      if(null != m_nextWalker)
        clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList);
  
      // If you don't check for the cloneList here, you'll go into an
      // recursive infinate loop.
      if(null != cloneList)
      {
        if(null != m_prevWalker)
          clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList);
      }
      else
      {
        if(null != m_nextWalker)
          clone.m_nextWalker.m_prevWalker = clone;
      }
      return clone;
    }
  
    /**
     * Find a clone that corresponds to the key argument.
     *
     * @param key The original AxesWalker for which there may be a clone.
     * @param cloneList vector of sources in odd elements, and the
     *                  corresponding clones in even vectors, may be null.
     *
     * @return A clone that corresponds to the key, or null if key not found.
     */
    static AxesWalker findClone(AxesWalker key, Vector cloneList)
    {
      if(null != cloneList)
      {
        // First, look for clone on list.
        int n = cloneList.size();
        for (int i = 0; i < n; i+=2)
        {
          if(key == cloneList.elementAt(i))
            return (AxesWalker)cloneList.elementAt(i+1);
        }
      }
      return null;
    }
  
    /**
     * Detaches the walker from the set which it iterated over, releasing
     * any computational resources and placing the iterator in the INVALID
     * state.
     */
    public void detach()
    {
          m_currentNode = DTM.NULL;
          m_dtm = null;
          m_traverser = null;
          m_isFresh = true;
          m_root = DTM.NULL;
    }
  
    //=============== TreeWalker Implementation ===============
  
    /**
     * The root node of the TreeWalker, as specified in setRoot(int root).
     * Note that this may actually be below the current node.
     *
     * @return The context node of the step.
     */
    public int getRoot()
    {
      return m_root;
    }
  
    /**
     * Get the analysis bits for this walker, as defined in the WalkerFactory.
     * @return One of WalkerFactory#BIT_DESCENDANT, etc.
     */
    public int getAnalysisBits()
    {
          int axis = getAxis();
          int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
          return bit;
    }
  
    /**
     * Set the root node of the TreeWalker.
     * (Not part of the DOM2 TreeWalker interface).
     *
     * @param root The context node of this step.
     */
    public void setRoot(int root)
    {
      // %OPT% Get this directly from the lpi.
      XPathContext xctxt = wi().getXPathContext();
      m_dtm = xctxt.getDTM(root);
      m_traverser = m_dtm.getAxisTraverser(m_axis);
      m_isFresh = true;
      m_foundLast = false;
      m_root = root;
      m_currentNode = root;
  
      if (DTM.NULL == root)
      {
        throw new RuntimeException(
          XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!");
      }
  
      resetProximityPositions();
    }
  
    /**
     * The node at which the TreeWalker is currently positioned.
     * <br> The value must not be null. Alterations to the DOM tree may cause
     * the current node to no longer be accepted by the TreeWalker's
     * associated filter. currentNode may also be explicitly set to any node,
     * whether or not it is within the subtree specified by the root node or
     * would be accepted by the filter and whatToShow flags. Further
     * traversal occurs relative to currentNode even if it is not part of the
     * current view by applying the filters in the requested direction (not
     * changing currentNode where no traversal is possible).
     *
     * @return The node at which the TreeWalker is currently positioned, only null
     * if setRoot has not yet been called.
     */
    public final int getCurrentNode()
    {
      return m_currentNode;
    }
  
    /**
     * Set the next walker in the location step chain.
     *
     *
     * @param walker Reference to AxesWalker derivative, or may be null.
     */
    public void setNextWalker(AxesWalker walker)
    {
      m_nextWalker = walker;
    }
  
    /**
     * Get the next walker in the location step chain.
     *
     *
     * @return Reference to AxesWalker derivative, or null.
     */
    public AxesWalker getNextWalker()
    {
      return m_nextWalker;
    }
  
    /**
     * Set or clear the previous walker reference in the location step chain.
     *
     *
     * @param walker Reference to previous walker reference in the location
     *               step chain, or null.
     */
    public void setPrevWalker(AxesWalker walker)
    {
      m_prevWalker = walker;
    }
  
    /**
     * Get the previous walker reference in the location step chain.
     *
     *
     * @return Reference to previous walker reference in the location
     *               step chain, or null.
     */
    public AxesWalker getPrevWalker()
    {
      return m_prevWalker;
    }
  
    /**
     * This is simply a way to bottle-neck the return of the next node, for
     * diagnostic purposes.
     *
     * @param n Node to return, or null.
     *
     * @return The argument.
     */
    private int returnNextNode(int n)
    {
  
      return n;
    }
  
    /**
     * Get the next node in document order on the axes.
     *
     * @return the next node in document order on the axes, or null.
     */
    protected int getNextNode()
    {
      if (m_foundLast)
        return DTM.NULL;
  
      if (m_isFresh)
      {
        m_currentNode = m_traverser.first(m_root);
        m_isFresh = false;
      }
      // I shouldn't have to do this the check for current node, I think.
      // numbering\numbering24.xsl fails if I don't do this.  I think
      // it occurs as the walkers are backing up. -sb
      else if(DTM.NULL != m_currentNode)
      {
        m_currentNode = m_traverser.next(m_root, m_currentNode);
      }
  
      if (DTM.NULL == m_currentNode)
        this.m_foundLast = true;
  
      return m_currentNode;
    }
  
    /**
     *  Moves the <code>TreeWalker</code> to the next visible node in document
     * order relative to the current node, and returns the new node. If the
     * current node has no next node,  or if the search for nextNode attempts
     * to step upward from the TreeWalker's root node, returns
     * <code>null</code> , and retains the current node.
     * @return  The new node, or <code>null</code> if the current node has no
     *   next node  in the TreeWalker's logical view.
     */
    public int nextNode()
    {
      int nextNode = DTM.NULL;
      AxesWalker walker = wi().getLastUsedWalker();
  
      while (true)
      {
        if (null == walker)
          break;
  
        nextNode = walker.getNextNode();
  
        if (DTM.NULL == nextNode)
        {
  
          walker = walker.m_prevWalker;
        }
        else
        {
          if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT)
          {
            continue;
          }
  
          if (null == walker.m_nextWalker)
          {
            wi().setLastUsedWalker(walker);
  
            // return walker.returnNextNode(nextNode);
            break;
          }
          else
          {
            AxesWalker prev = walker;
  
            walker = walker.m_nextWalker;
  
            walker.setRoot(nextNode);
  
            walker.m_prevWalker = prev;
  
            continue;
          }
        }  // if(null != nextNode)
      }  // while(null != walker)
  
      return nextNode;
    }
  
    //============= End TreeWalker Implementation =============
  
    /**
     * Get the index of the last node that can be itterated to.
     *
     *
     * @param xctxt XPath runtime context.
     *
     * @return the index of the last node that can be itterated to.
     */
    public int getLastPos(XPathContext xctxt)
    {
  
      int pos = getProximityPosition();
  
      AxesWalker walker;
  
      try
      {
        walker = (AxesWalker) clone();
      }
      catch (CloneNotSupportedException cnse)
      {
        return -1;
      }
  
      walker.setPredicateCount(m_predicateIndex);
      walker.setNextWalker(null);
      walker.setPrevWalker(null);
  
      WalkingIterator lpi = wi();
      AxesWalker savedWalker = lpi.getLastUsedWalker();
  
      try
      {
        lpi.setLastUsedWalker(walker);
  
        int next;
  
        while (DTM.NULL != (next = walker.nextNode()))
        {
          pos++;
        }
  
        // TODO: Should probably save this in the iterator.
      }
      finally
      {
        lpi.setLastUsedWalker(savedWalker);
      }
  
      // System.out.println("pos: "+pos);
      return pos;
    }
  
    //============= State Data =============
  
    /**
     * The DTM for the root.  This can not be used, or must be changed,
     * for the filter walker, or any walker that can have nodes
     * from multiple documents.
     * Never, ever, access this value without going through getDTM(int node).
     */
    private DTM m_dtm;
  
    /**
     * Set the DTM for this walker.
     *
     * @param dtm Non-null reference to a DTM.
     */
    public void setDefaultDTM(DTM dtm)
    {
      m_dtm = dtm;
    }
  
    /**
     * Get the DTM for this walker.
     *
     * @return Non-null reference to a DTM.
     */
    public DTM getDTM(int node)
    {
      //
      return wi().getXPathContext().getDTM(node);
    }
  
    /**
     * Returns true if all the nodes in the iteration well be returned in document
     * order.
     * Warning: This can only be called after setRoot has been called!
     *
     * @return true as a default.
     */
    public boolean isDocOrdered()
    {
      return true;
    }
  
    /**
     * Returns the axis being iterated, if it is known.
     *
     * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
     * types.
     */
    public int getAxis()
    {
      return m_axis;
    }
  
    /**
     * This will traverse the heararchy, calling the visitor for
     * each member.  If the called visitor method returns
     * false, the subtree should not be called.
     *
     * @param owner The owner of the visitor, where that path may be
     *              rewritten if needed.
     * @param visitor The visitor whose appropriate method will be called.
     */
    public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
    {
          if(visitor.visitStep(owner, this))
          {
                  callPredicateVisitors(visitor);
                  if(null != m_nextWalker)
                  {
                          m_nextWalker.callVisitors(this, visitor);
                  }
          }
    }
  
    /**
     * @see ExpressionOwner#getExpression()
     */
    public Expression getExpression()
    {
      return m_nextWalker;
    }
  
    /**
     * @see ExpressionOwner#setExpression(Expression)
     */
    public void setExpression(Expression exp)
    {
          exp.exprSetParent(this);
          m_nextWalker = (AxesWalker)exp;
    }
  
      /**
       * @see Expression#deepEquals(Expression)
       */
      public boolean deepEquals(Expression expr)
      {
        if (!super.deepEquals(expr))
                  return false;
  
        AxesWalker walker = (AxesWalker)expr;
        if(this.m_axis != walker.m_axis)
          return false;
  
        return true;
      }
  
    /**
     *  The root node of the TreeWalker, as specified when it was created.
     */
    transient int m_root = DTM.NULL;
  
    /**
     *  The node at which the TreeWalker is currently positioned.
     */
    private transient int m_currentNode = DTM.NULL;
  
    /** True if an itteration has not begun.  */
    transient boolean m_isFresh;
  
    /** The next walker in the location step chain.
     *  @serial  */
    protected AxesWalker m_nextWalker;
  
    /** The previous walker in the location step chain, or null.
     *  @serial   */
    AxesWalker m_prevWalker;
  
    /** The traversal axis from where the nodes will be filtered. */
    protected int m_axis = -1;
  
    /** The DTM inner traversal class, that corresponds to the super axis. */
    protected DTMAxisTraverser m_traverser;
  }
  