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

org.htmlunit.xpath.axes.PredicatedNodeTest Maven / Gradle / Ivy

There is a newer version: 4.7.0
Show newest version
/*
 * 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.
 */
package org.htmlunit.xpath.axes;

import org.htmlunit.xpath.Expression;
import org.htmlunit.xpath.XPathContext;
import org.htmlunit.xpath.XPathVisitor;
import org.htmlunit.xpath.compiler.Compiler;
import org.htmlunit.xpath.objects.XObject;
import org.htmlunit.xpath.patterns.NodeTest;
import org.htmlunit.xpath.xml.dtm.DTM;
import org.htmlunit.xpath.xml.dtm.DTMIterator;

public abstract class PredicatedNodeTest extends NodeTest implements SubContextList {

  /**
   * Construct an AxesWalker using a LocPathIterator.
   *
   * @param locPathIterator non-null reference to the parent iterator.
   */
  PredicatedNodeTest(final LocPathIterator locPathIterator) {
    m_lpi = locPathIterator;
  }

  /** Construct an AxesWalker. The location path iterator will have to be set before use. */
  PredicatedNodeTest() {
  }

  /** {@inheritDoc} */
  @Override
  public Object clone() throws CloneNotSupportedException {
    // Do not access the location path itterator during this operation!

    final PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();

    if ((null != this.m_proximityPositions)
        && (this.m_proximityPositions == clone.m_proximityPositions)) {
      clone.m_proximityPositions = new int[this.m_proximityPositions.length];

      System.arraycopy(
          this.m_proximityPositions,
          0,
          clone.m_proximityPositions,
          0,
          this.m_proximityPositions.length);
    }

    if (clone.m_lpi == this) {
        clone.m_lpi = (LocPathIterator) clone;
    }

    return clone;
  }

  // Only for clones for findLastPos. See bug4638.
  protected int m_predCount = -1;

  /**
   * Get the number of predicates that this walker has.
   *
   * @return the number of predicates that this walker has.
   */
  public int getPredicateCount() {
    if (-1 == m_predCount) {
        return (null == predicates_) ? 0 : predicates_.length;
    }
    return m_predCount;
  }

  /**
   * Set the number of predicates that this walker has. This does more that one would think, as it
   * creates a new predicate array of the size of the count argument, and copies count predicates
   * into the new one from the old, and then reassigns the predicates value. All this to keep from
   * having to have a predicate count value.
   *
   * @param count The number of predicates, which must be equal or less than the existing count.
   */
  public void setPredicateCount(final int count) {
    if (count > 0) {
      final Expression[] newPredicates = new Expression[count];
      System.arraycopy(predicates_, 0, newPredicates, 0, count);
      predicates_ = newPredicates;
    }
    else {
        predicates_ = null;
    }
  }

  /**
   * Init predicate info.
   *
   * @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.
   * @throws javax.xml.transform.TransformerException if any
   */
  protected void initPredicateInfo(final Compiler compiler, final int opPos)
      throws javax.xml.transform.TransformerException {

    final int pos = compiler.getFirstPredicateOpPos(opPos);

    if (pos > 0) {
      predicates_ = compiler.getCompiledPredicates(pos);
      if (null != predicates_) {
        for (final Expression predicate : predicates_) {
          predicate.exprSetParent(this);
        }
      }
    }
  }

  /**
   * Get a predicate expression at the given index.
   *
   * @param index Index of the predicate.
   * @return A predicate expression.
   */
  public Expression getPredicate(final int index) {
    return predicates_[index];
  }

  /**
   * Get the current sub-context position.
   *
   * @return The node position of this walker in the sub-context node list.
   */
  public int getProximityPosition() {
    return getProximityPosition(m_predicateIndex);
  }

  /** {@inheritDoc} */
  @Override
  public int getProximityPosition(final XPathContext xctxt) {
    return getProximityPosition();
  }

  /** {@inheritDoc} */
  @Override
  public abstract int getLastPos(XPathContext xctxt);

  /**
   * Get the current sub-context position.
   *
   * @param predicateIndex The index of the predicate where the proximity should be taken from.
   * @return The node position of this walker in the sub-context node list.
   */
  protected int getProximityPosition(final int predicateIndex) {
    return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
  }

  /** Reset the proximity positions counts. */
  public void resetProximityPositions() {
    final int nPredicates = getPredicateCount();
    if (nPredicates > 0) {
      if (null == m_proximityPositions) {
        m_proximityPositions = new int[nPredicates];
    }

      for (int i = 0; i < nPredicates; i++) {
        try {
          initProximityPosition(i);
        }
        catch (final Exception e) {
          // TODO: Fix this...
          throw new org.htmlunit.xpath.xml.utils.WrappedRuntimeException(e);
        }
      }
    }
  }

  /**
   * Init the proximity position to zero for a forward axes.
   *
   * @param i The index into the m_proximityPositions array.
   */
  public void initProximityPosition(final int i) {
    m_proximityPositions[i] = 0;
  }

  /**
   * Count forward one proximity position.
   *
   * @param i The index into the m_proximityPositions array, where the increment will occur.
   */
  protected void countProximityPosition(final int i) {
    // Note that in the case of a UnionChildIterator, this may be a
    // static object and so m_proximityPositions may indeed be null!
    final int[] pp = m_proximityPositions;
    if ((null != pp) && (i < pp.length)) {
        pp[i]++;
    }
  }

  /**
   * Tells if this is a reverse axes.
   *
   * @return false, unless a derived class overrides.
   */
  public boolean isReverseAxes() {
    return false;
  }

  /**
   * Process the predicates.
   *
   * @param context The current context node.
   * @param xctxt The XPath runtime context.
   * @return the result of executing the predicate expressions.
   * @throws javax.xml.transform.TransformerException if any
   */
  boolean executePredicates(final int context, final XPathContext xctxt)
      throws javax.xml.transform.TransformerException {

    final int nPredicates = getPredicateCount();
    // System.out.println("nPredicates: "+nPredicates);
    if (nPredicates == 0) {
        return true;
    }

    try {
      m_predicateIndex = 0;
      xctxt.pushSubContextList(this);
      xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
      xctxt.pushCurrentNode(context);

      for (int i = 0; i < nPredicates; i++) {
        final XObject pred = predicates_[i].execute(xctxt);
        // System.out.println("\nBack from executing predicate expression - waiting
        // count:
        // "+m_lpi.getWaitingCount());
        // System.out.println("pred.getType(): "+pred.getType());
        if (XObject.CLASS_NUMBER == pred.getType()) {
          if (DEBUG_PREDICATECOUNTING) {
            System.out.flush();
            System.out.println("\n===== start predicate count ========");
            System.out.println("m_predicateIndex: " + m_predicateIndex);
            // System.out.println("getProximityPosition(m_predicateIndex): "
            // + getProximityPosition(m_predicateIndex));
            System.out.println("pred.num(): " + pred.num());
          }

          final int proxPos = this.getProximityPosition(m_predicateIndex);
          final int predIndex = (int) pred.num();
          if (proxPos != predIndex) {
            if (DEBUG_PREDICATECOUNTING) {
              System.out.println("\nnode context: " + nodeToString(context));
              System.out.println("index predicate is false: " + proxPos);
              System.out.println("\n===== end predicate count ========");
            }
            return false;
          }
          else if (DEBUG_PREDICATECOUNTING) {
            System.out.println("\nnode context: " + nodeToString(context));
            System.out.println("index predicate is true: " + proxPos);
            System.out.println("\n===== end predicate count ========");
          }

          // If there is a proximity index that will not change during the
          // course of itteration, then we know there can be no more true
          // occurances of this predicate, so flag that we're done after
          // this.
          //
          // bugzilla 14365
          // We can't set m_foundLast = true unless we're sure that -all-
          // remaining parameters are stable, or else last() fails. Fixed so
          // only sets m_foundLast if on the last predicate
          if (predicates_[i].isStableNumber() && i == nPredicates - 1) {
            m_foundLast = true;
          }
        }
        else if (!pred.bool()) {
            return false;
        }

        countProximityPosition(++m_predicateIndex);
      }
    }
    finally {
      xctxt.popCurrentNode();
      xctxt.popNamespaceContext();
      xctxt.popSubContextList();
      m_predicateIndex = -1;
    }

    return true;
  }

  /**
   * Diagnostics.
   *
   * @param n Node to give diagnostic information about, or null.
   * @return Informative string about the argument.
   */
  protected String nodeToString(final int n) {
    if (DTM.NULL != n) {
      final DTM dtm = m_lpi.getXPathContext().getDTM(n);
      return dtm.getNodeName(n) + "{" + (n + 1) + "}";
    }
    return "null";
  }

  // =============== NodeFilter Implementation ===============

  /**
   * Test whether a specified node is visible in the logical view of a TreeWalker or NodeIterator.
   * This function will be called by the implementation of TreeWalker and NodeIterator; it is not
   * intended to be called directly from user code.
   *
   * @param n The node to check to see if it passes the filter or not.
   * @return a constant to determine whether the node is accepted, rejected, or skipped, as defined
   *     above .
   */
  public short acceptNode(final int n) {

    final XPathContext xctxt = m_lpi.getXPathContext();

    try {
      xctxt.pushCurrentNode(n);

      final XObject score = execute(xctxt, n);

      // System.out.println("\n::acceptNode - score: "+score.num()+"::");
      if (score != NodeTest.SCORE_NONE) {
        if (getPredicateCount() > 0) {
          countProximityPosition(0);

          if (!executePredicates(n, xctxt)) {
            return DTMIterator.FILTER_SKIP;
        }
        }

        return DTMIterator.FILTER_ACCEPT;
      }
    }
    catch (final javax.xml.transform.TransformerException se) {

      // TODO: Fix this.
      throw new RuntimeException(se.getMessage());
    }
    finally {
      xctxt.popCurrentNode();
    }

    return DTMIterator.FILTER_SKIP;
  }

  /**
   * Set the location path iterator owner for this walker. Besides initialization, this function is
   * called during cloning operations.
   *
   * @param li non-null reference to the owning location path iterator.
   */
  public void setLocPathIterator(final LocPathIterator li) {
    m_lpi = li;
    if (this != li) {
        li.exprSetParent(this);
    }
  }

  /** {@inheritDoc} */
  @Override
  public boolean canTraverseOutsideSubtree() {
    final int n = getPredicateCount();
    for (int i = 0; i < n; i++) {
      if (getPredicate(i).canTraverseOutsideSubtree()) {
        return true;
    }
    }
    return false;
  }

  /**
   * 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 visitor The visitor whose appropriate method will be called.
   */
  public void callPredicateVisitors(final XPathVisitor visitor) {
    if (null != predicates_) {
      for (final Expression predicate : predicates_) {
        if (visitor.visitPredicate(predicate)) {
          predicate.callVisitors(visitor);
        }
      }
    }
  }

  /** {@inheritDoc} */
  @Override
  public boolean deepEquals(final Expression expr) {
    if (!super.deepEquals(expr)) {
        return false;
    }

    final PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
    if (null != predicates_) {

      final int n = predicates_.length;
      if ((null == pnt.predicates_) || (pnt.predicates_.length != n)) {
        return false;
    }
      for (int i = 0; i < n; i++) {
        if (!predicates_[i].deepEquals(pnt.predicates_[i])) {
            return false;
        }
      }
    }
    else if (null != pnt.predicates_) {
        return false;
    }

    return true;
  }

  /** This is true if nextNode returns null. */
  protected transient boolean m_foundLast = false;

  /**
   * The owning location path iterator.
   *
   * @serial
   */
  protected LocPathIterator m_lpi;

  /** Which predicate we are executing. */
  transient int m_predicateIndex = -1;

  /**
   * The list of predicate expressions. Is static and does not need to be deep cloned.
   *
   * @serial
   */
  private Expression[] predicates_;

  /** An array of counts that correspond to the number of predicates the step contains. */
  protected transient int[] m_proximityPositions;

  /** If true, diagnostic messages about predicate execution will be posted. */
  static final boolean DEBUG_PREDICATECOUNTING = false;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy