All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xpath.axes.AxesWalker Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 513117 2007-03-01 03:28:52Z minchau $
 */
package org.apache.xpath.axes;

import java.util.Vector;

import org.apache.xalan.res.XSLMessages;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMAxisTraverser;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.XPathContext;
import org.apache.xpath.XPathVisitor;
import org.apache.xpath.compiler.Compiler;
import org.apache.xpath.res.XPATHErrorResources;

/**
 * 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 javax.xml.transform.TransformerException
   */
  public void init(Compiler compiler, int opPos, int stepType)
          throws javax.xml.transform.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.
   * 
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 TreeWalker 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 * null , and retains the current node. * @return The new node, or null 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; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy