/** * 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++; } } }
/** * 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; }