package client.net.sf.saxon.ce.functions; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.expr.sort.DocumentOrderIterator; import client.net.sf.saxon.ce.expr.sort.LocalOrderComparer; import client.net.sf.saxon.ce.om.DocumentInfo; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.om.NodeInfo; import client.net.sf.saxon.ce.om.SequenceIterator; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.iter.ListIterator; import client.net.sf.saxon.ce.tree.iter.SingletonIterator; import client.net.sf.saxon.ce.tree.util.StringTokenizer; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.value.AtomicValue; import client.net.sf.saxon.ce.value.StringValue; import client.net.sf.saxon.ce.value.Whitespace; import java.util.ArrayList; import java.util.List; /** * The XPath id() or element-with-id() function (synonymous for a non-schema-aware processor) * XPath 2.0 version: accepts any sequence as the first parameter; each item in the sequence * is taken as an IDREFS value, that is, a space-separated list of ID values. * Also accepts an optional second argument to identify the target document, this * defaults to the context node. */ public class Id extends SystemFunction { public Id newInstance() { return new Id(); } /** * Simplify: add a second implicit argument, the context document * @param visitor an expression visitor */ public Expression simplify(ExpressionVisitor visitor) throws XPathException { Id id = (Id)super.simplify(visitor); if (argument.length == 1) { id.addContextDocumentArgument(1, getFunctionName().getLocalName()); } return id; } /** * Type-check the expression. This also calls preEvaluate() to evaluate the function * if all the arguments are constant; functions that do not require this behavior * can override the preEvaluate method. */ public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { if (argument[1] instanceof RootExpression && contextItemType != null && contextItemType.isAtomicType()) { // intercept this to get better diagnostics typeError(getFunctionName().getLocalName() + "() function called when the context item is not a node", "XPTY0004", null); } return super.typeCheck(visitor, contextItemType); } /** * Static analysis: prevent sorting of the argument */ public void checkArguments(ExpressionVisitor visitor) throws XPathException { super.checkArguments(visitor); Optimizer opt = visitor.getConfiguration().getOptimizer(); argument[0] = ExpressionTool.unsorted(opt, argument[0], false); } /** * preEvaluate: this method suppresses compile-time evaluation by doing nothing * @param visitor an expression visitor */ public Expression preEvaluate(ExpressionVisitor visitor) { return this; } /** * Get the static properties of this expression (other than its type). The result is * bit-signficant. These properties are used for optimizations. In general, if * property bit is set, it is true, but if it is unset, the value is unknown. */ public int computeSpecialProperties() { int prop = StaticProperty.ORDERED_NODESET | StaticProperty.SINGLE_DOCUMENT_NODESET | StaticProperty.NON_CREATIVE; if ((getNumberOfArguments() == 1) || (argument[1].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) { prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET; } return prop; } /** * Evaluate the function to return an iteration of selected nodes. */ public SequenceIterator iterate(XPathContext context) throws XPathException { NodeInfo arg1; try { arg1 = (NodeInfo)argument[1].evaluateItem(context); } catch (XPathException e) { if (context.getContextItem() instanceof AtomicValue) { // Override the unhelpful message that trickles down.. dynamicError("For the " + getFunctionName().getLocalName() + "() function, the context item is not a node", "XPTY0004", context); return null; } else { throw e; } } arg1 = arg1.getRoot(); if (arg1.getNodeKind() != Type.DOCUMENT) { dynamicError("In the " + getFunctionName().getLocalName() + "() function," + " the tree being searched must be one whose root is a document node", "FODC0001", context); return null; } DocumentInfo doc = (DocumentInfo)arg1; SequenceIterator idrefs = argument[0].iterate(context); IdMappingFunction map = new IdMappingFunction(); map.document = doc; SequenceIterator result = new MappingIterator(idrefs, map); return new DocumentOrderIterator(result, LocalOrderComparer.getInstance()); } private static class IdMappingFunction implements MappingFunction { public DocumentInfo document; /** * Evaluate the function for a single string value * (implements the MappingFunction interface) */ public SequenceIterator map(Item item) throws XPathException { String idrefs = Whitespace.trim(item.getStringValueCS()); // If this value contains a space, we need to break it up into its // separate tokens; if not, we can process it directly if (Whitespace.containsWhitespace(idrefs)) { List<StringValue> refs = new ArrayList<StringValue>(10); StringTokenizer st = new StringTokenizer(idrefs); while (st.hasMoreTokens()) { refs.add(StringValue.makeStringValue(st.nextToken())); } IdMappingFunction submap = new IdMappingFunction(); submap.document = document; return new MappingIterator(new ListIterator(refs), submap); } else { return SingletonIterator.makeIterator(document.selectID(idrefs)); } } } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.