/** * Get simple node number. This is defined as one plus the number of previous siblings of the same * node type and name. It is not accessible directly in XSL. * * @param node The node whose number is required * @param controller Used for remembering previous result, for performance * @exception XPathException if any error occurs * @return the node number, as defined above */ public static int getNumberSimple(NodeInfo node, Controller controller) throws XPathException { // checkNumberable(node); int fingerprint = node.getFingerprint(); NodeTest same; if (fingerprint == -1) { same = NodeKindTest.makeNodeKindTest(node.getNodeKind()); } else { same = new NameTest(node); } SequenceIterator preceding = node.iterateAxis(Axis.PRECEDING_SIBLING, same); int i = 1; while (true) { NodeInfo prev = (NodeInfo) preceding.next(); if (prev == null) { break; } int memo = controller.getRememberedNumber(prev); if (memo > 0) { memo += i; controller.setRememberedNumber(node, memo); return memo; } i++; } controller.setRememberedNumber(node, i); return i; }
/** * Execute the compiled Query, returning the first item in the result. This is useful where it is * known that the expression will only return a singleton value (for example, a single node, or a * boolean). * * @param env Provides the dynamic query evaluation context * @return The first item in the sequence returned by the expression. If the expression returns an * empty sequence, this method returns null. Otherwise, it returns the first item in the * result sequence, represented as a Java object using the same mapping as for the {@link * XQueryExpression#evaluate evaluate} method */ public Object evaluateSingle(DynamicQueryContext env) throws XPathException { if (isUpdating) { throw new XPathException("Cannot call evaluateSingle() on an updating query"); } SequenceIterator iterator = iterator(env); Item item = iterator.next(); if (item == null) { return null; } return Value.convertToJava(item); }
public Item next() throws XPathException { try { return base.next(); } catch (XPathException e1) { e1.maybeSetLocation(expression); try { listener.fatalError(e1); } catch (TransformerException e2) { // } e1.setHasBeenReported(); throw e1; } }
/** * Execute a the compiled Query, returning the results as a List. * * @param env Provides the dynamic query evaluation context * @return The results of the expression, as a List. The List represents the sequence of items * returned by the expression. Each item in the list will either be an object representing a * node, or an object representing an atomic value. For the types of Java object that may be * returned, see the description of the {@link org.orbeon.saxon.xpath.XPathEvaluator#evaluate * evaluate} method of class XPathProcessor */ public List evaluate(DynamicQueryContext env) throws XPathException { if (isUpdating) { throw new XPathException("Cannot call evaluate() on an updating query"); } SequenceIterator iterator = iterator(env); ArrayList list = new ArrayList(100); while (true) { Item item = iterator.next(); if (item == null) { return list; } list.add(Value.convertToJava(item)); } }
/** * Show a query producing a sequence as its result and returning the sequence to the Java * application in the form of an iterator. For each item in the result, its string value is * output. */ public static void exampleToSequence() throws XPathException { final Configuration config = new Configuration(); final StaticQueryContext sqc = config.newStaticQueryContext(); final XQueryExpression exp = sqc.compileQuery("for $i in 1 to 10 return ($i * $i)"); final DynamicQueryContext dynamicContext = new DynamicQueryContext(config); final SequenceIterator iter = exp.iterator(dynamicContext); while (true) { Item item = iter.next(); if (item == null) { break; } System.out.println(item.getStringValue()); } }
/** * 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++; } } }
/** * Convert an XPath value to an object in this object model. If the supplied value can be * converted to an object in this model, of the specified class, then the conversion should be * done and the resulting object returned. If the value cannot be converted, the method should * return null. Note that the supplied class might be a List, in which case the method should * inspect the contents of the Value to see whether they belong to this object model. * * @throws XPathException if the target class is explicitly associated with this object model, but * the supplied value cannot be converted to the appropriate class */ public Object convertXPathValueToObject(Value value, Class target, XPathContext context) throws XPathException { // We accept the object if (a) the target class is Node, Node[], or NodeList, // or (b) the supplied object is a node, or sequence of nodes, that wrap DOM nodes, // provided that the target class is Object or a collection class boolean requireDOM = (Node.class.isAssignableFrom(target) || (target == NodeList.class) || (target.isArray() && Node.class.isAssignableFrom(target.getComponentType()))); // Note: we allow the declared type of the method argument to be a subclass of Node. If the // actual // node supplied is the wrong kind of node, this will result in a Java exception. boolean allowDOM = (target == Object.class || target.isAssignableFrom(ArrayList.class) || target.isAssignableFrom(HashSet.class) || (target.isArray() && target.getComponentType() == Object.class)); if (!(requireDOM || allowDOM)) { return null; } List nodes = new ArrayList(20); SequenceIterator iter = value.iterate(context); while (true) { Item item = iter.next(); if (item == null) { break; } if (item instanceof VirtualNode) { Object o = ((VirtualNode) item).getUnderlyingNode(); if (o instanceof Node) { nodes.add(o); } else { if (requireDOM) { DynamicError err = new DynamicError( "Extension function required class " + target.getName() + "; supplied value of class " + item.getClass().getName() + " could not be converted"); throw err; } ; } } else if (requireDOM) { if (item instanceof NodeInfo) { nodes.add(NodeOverNodeInfo.wrap((NodeInfo) item)); } else { DynamicError err = new DynamicError( "Extension function required class " + target.getName() + "; supplied value of class " + item.getClass().getName() + " could not be converted"); throw err; } } else { return null; // DOM Nodes are not actually required; let someone else try the conversion } } if (nodes.size() == 0 && !requireDOM) { return null; // empty sequence supplied - try a different mapping } if (Node.class.isAssignableFrom(target)) { if (nodes.size() != 1) { DynamicError err = new DynamicError( "Extension function requires a single DOM Node" + "; supplied value contains " + nodes.size() + " nodes"); throw err; } return nodes.get(0); // could fail if the node is of the wrong kind } else if (target == NodeList.class) { return new DOMNodeList(nodes); } else if (target.isArray() && target.getComponentType() == Node.class) { Node[] array = new Node[nodes.size()]; nodes.toArray(array); return array; } else if (target.isAssignableFrom(ArrayList.class)) { return nodes; } else if (target.isAssignableFrom(HashSet.class)) { return new HashSet(nodes); } else { // after all this work, give up return null; } }
/** Evaluate as an expression. */ public Item evaluateItem(XPathContext context) throws XPathException { if (isLazyConstruction() && (context.getConfiguration().areAllNodesUntyped() || (validation == Validation.PRESERVE && getSchemaType() == null))) { return new UnconstructedDocument(this, context); } else { Controller controller = context.getController(); DocumentInfo root; if (textOnly) { CharSequence textValue; if (constantText != null) { textValue = constantText; } else { FastStringBuffer sb = new FastStringBuffer(100); SequenceIterator iter = content.iterate(context); while (true) { Item item = iter.next(); if (item == null) break; sb.append(item.getStringValueCS()); } textValue = sb.condense(); } root = new TextFragmentValue(textValue, getBaseURI()); ((TextFragmentValue) root).setConfiguration(controller.getConfiguration()); } else { try { XPathContext c2 = context.newMinorContext(); c2.setOrigin(this); Builder builder = controller.makeBuilder(); // builder.setSizeParameters(treeSizeParameters); builder.setLineNumbering(controller.getConfiguration().isLineNumbering()); // receiver.setSystemId(getBaseURI()); builder.setBaseURI(getBaseURI()); builder.setTiming(false); PipelineConfiguration pipe = controller.makePipelineConfiguration(); pipe.setHostLanguage(getHostLanguage()); // pipe.setBaseURI(baseURI); builder.setPipelineConfiguration(pipe); c2.changeOutputDestination( null, builder, false, getHostLanguage(), validation, getSchemaType()); Receiver out = c2.getReceiver(); out.open(); out.startDocument(0); content.process(c2); out.endDocument(); out.close(); root = (DocumentInfo) builder.getCurrentRoot(); } catch (XPathException e) { e.maybeSetLocation(this); e.maybeSetContext(context); throw e; } } return root; } }
/** * 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; }