Example #1
0
  /**
   * Get node number (level="single"). If the current node matches the supplied pattern, the
   * returned number is one plus the number of previous siblings that match the pattern. Otherwise,
   * return the element number of the nearest ancestor that matches the supplied pattern.
   *
   * @param node the current node, the one whose node number is required
   * @param count Pattern that identifies which nodes should be counted. Default (null) is the
   *     element name if the current node is an element, or "node()" otherwise.
   * @param from Pattern that specifies where counting starts from. Default (null) is the root node.
   *     (This parameter does not seem useful but is included for the sake of XSLT conformance.)
   * @param controller the controller of the transformation, used if the patterns reference context
   *     values (e.g. variables)
   * @exception XPathException when any error occurs in processing
   * @return the node number established as follows: go to the nearest ancestor-or-self that matches
   *     the 'count' pattern and that is a descendant of the nearest ancestor that matches the
   *     'from' pattern. Return one plus the nunber of preceding siblings of that ancestor that
   *     match the 'count' pattern. If there is no such ancestor, return 0.
   */
  public static int getNumberSingle(
      NodeInfo node, Pattern count, Pattern from, Controller controller) throws XPathException {

    //        checkNumberable(node);

    if (count == null && from == null) {
      return getNumberSimple(node, controller);
    }

    boolean knownToMatch = false;
    if (count == null) {
      if (node.getFingerprint() == -1) { // unnamed node
        count = NodeKindTest.makeNodeKindTest(node.getNodeKind());
      } else {
        count = new NameTest(node);
      }
      knownToMatch = true;
    }

    NodeInfo target = node;
    while (!(knownToMatch || count.matches(target, controller))) {
      target = target.getParent();
      if (target == null) {
        return 0;
      }
      if (from != null && from.matches(target, controller)) {
        return 0;
      }
    }

    // we've found the ancestor to count from

    SequenceIterator preceding = target.iterateAxis(Axis.PRECEDING_SIBLING, count.getNodeTest());
    // pass the filter condition down to the axis enumeration where possible
    boolean alreadyChecked = (count instanceof NodeTest);
    int i = 1;
    while (true) {
      NodeInfo p = (NodeInfo) preceding.next();
      if (p == null) {
        return i;
      }
      if (alreadyChecked || count.matches(p, controller)) {
        i++;
      }
    }
  }
Example #2
0
  /**
   * Get node number (level="any"). Return one plus the number of previous nodes in the document
   * that match the supplied pattern
   *
   * @exception XPathException
   * @param inst Identifies the xsl:number instruction; this is relevant when the function is
   *     memoised to support repeated use of the same instruction to number modulple nodes
   * @param node Identifies the xsl:number instruction; this is relevant when the function is
   *     memoised to support repeated use of the same instruction to number modulple nodes
   * @param count Pattern that identifies which nodes should be counted. Default (null) is the
   *     element name if the current node is an element, or "node()" otherwise.
   * @param from Pattern that specifies where counting starts from. Default (null) is the root node.
   *     Only nodes after the first (most recent) node that matches the 'from' pattern are counted.
   * @param controller The controller
   * @param hasVariablesInPatterns if the count or from patterns contain variables, then it's not
   *     safe to get the answer by adding one to the number of the most recent node that matches
   * @return one plus the number of nodes that precede the current node, that match the count
   *     pattern, and that follow the first node that matches the from pattern if specified.
   */
  public static int getNumberAny(
      Instruction inst,
      NodeInfo node,
      Pattern count,
      Pattern from,
      Controller controller,
      boolean hasVariablesInPatterns)
      throws XPathException {

    NodeInfo memoNode = null;
    int memoNumber = 0;
    boolean memoise = (!hasVariablesInPatterns && count != null);
    if (memoise) {
      Object[] memo = (Object[]) controller.getUserData(inst, "xsl:number");
      if (memo != null) {
        memoNode = (NodeInfo) memo[0];
        memoNumber = ((Integer) memo[1]).intValue();
      }
    }

    int num = 0;
    if (count == null) {
      if (node.getFingerprint() == -1) { // unnamed node
        count = NodeKindTest.makeNodeKindTest(node.getNodeKind());
      } else {
        count = new NameTest(node);
      }
      num = 1;
    } else if (count.matches(node, controller)) {
      num = 1;
    }

    // We use a special axis invented for the purpose: the union of the preceding and
    // ancestor axes, but in reverse document order

    // Pass part of the filtering down to the axis iterator if possible
    NodeTest filter;
    if (from == null) {
      filter = count.getNodeTest();
    } else if (from.getNodeKind() == Type.ELEMENT && count.getNodeKind() == Type.ELEMENT) {
      filter = NodeKindTest.ELEMENT;
    } else {
      filter = AnyNodeTest.getInstance();
    }

    SequenceIterator preceding = node.iterateAxis(Axis.PRECEDING_OR_ANCESTOR, filter);

    while (true) {
      NodeInfo prev = (NodeInfo) preceding.next();
      if (prev == null) {
        break;
      }
      if (from != null && from.matches(prev, controller)) {
        return num;
      }
      if (count.matches(prev, controller)) {
        if (num == 1 && memoNode != null && prev.isSameNode(memoNode)) {
          num = memoNumber + 1;
          break;
        }
        num++;
      }
    }
    if (memoise) {
      Object[] memo = new Object[2];
      memo[0] = node;
      memo[1] = new Integer(num);
      controller.setUserData(inst, "xsl:number", memo);
    }
    return num;
  }