001: package net.sf.saxon.functions;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.om.*;
005: import net.sf.saxon.sort.DocumentOrderIterator;
006: import net.sf.saxon.sort.LocalOrderComparer;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.value.AtomicValue;
009: import net.sf.saxon.value.Cardinality;
010: import net.sf.saxon.type.Type;
011:
012: /**
013: * The XPath id() function
014: * XPath 2.0 version: accepts any sequence as the first parameter; each item in the sequence
015: * is taken as an IDREFS value, that is, a space-separated list of ID values.
016: * Also accepts an optional second argument to identify the target document, this
017: * defaults to the context node.
018: */
019:
020: public class Id extends SystemFunction {
021:
022: private boolean isSingletonId = false;
023:
024: /**
025: * Simplify: add a second implicit argument, the context document
026: */
027:
028: public Expression simplify(StaticContext env) throws XPathException {
029: Id id = (Id) super .simplify(env);
030: if (argument.length == 1) {
031: id.addContextDocumentArgument(1, "id");
032: }
033: return id;
034: }
035:
036: /**
037: * Static analysis: prevent sorting of the argument
038: */
039:
040: public void checkArguments(StaticContext env) throws XPathException {
041: super .checkArguments(env);
042: Optimizer opt = env.getConfiguration().getOptimizer();
043: argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
044: isSingletonId = !Cardinality.allowsMany(argument[0]
045: .getCardinality());
046: }
047:
048: /**
049: * preEvaluate: this method suppresses compile-time evaluation by doing nothing
050: */
051:
052: public Expression preEvaluate(StaticContext env) {
053: return this ;
054: }
055:
056: /**
057: * Get the static properties of this expression (other than its type). The result is
058: * bit-signficant. These properties are used for optimizations. In general, if
059: * property bit is set, it is true, but if it is unset, the value is unknown.
060: */
061:
062: public int computeSpecialProperties() {
063: int prop = StaticProperty.ORDERED_NODESET
064: | StaticProperty.SINGLE_DOCUMENT_NODESET
065: | StaticProperty.NON_CREATIVE;
066: if ((getNumberOfArguments() == 1)
067: || (argument[1].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
068: prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
069: }
070: return prop;
071: }
072:
073: /**
074: * Evaluate the function to return an iteration of selected nodes.
075: */
076:
077: public SequenceIterator iterate(XPathContext context)
078: throws XPathException {
079:
080: NodeInfo arg1 = (NodeInfo) argument[1].evaluateItem(context);
081: arg1 = arg1.getRoot();
082: if (arg1.getNodeKind() != Type.DOCUMENT) {
083: dynamicError(
084: "In the id() function,"
085: + " the tree being searched must be one whose root is a document node",
086: context);
087: return null;
088: }
089: DocumentInfo doc = (DocumentInfo) arg1;
090:
091: if (isSingletonId) {
092: AtomicValue arg = (AtomicValue) argument[0]
093: .evaluateItem(context);
094: if (arg == null) {
095: return EmptyIterator.getInstance();
096: }
097: String idrefs = arg.getStringValue();
098: if (idrefs.indexOf(0x20) >= 0 || idrefs.indexOf(0x09) >= 0
099: || idrefs.indexOf(0x0a) >= 0
100: || idrefs.indexOf(0x0d) >= 0) {
101: StringTokenIterator tokens = new StringTokenIterator(
102: idrefs);
103: IdMappingFunction map = new IdMappingFunction();
104: map.document = doc;
105: SequenceIterator result = new MappingIterator(tokens,
106: map, null);
107: return new DocumentOrderIterator(result,
108: LocalOrderComparer.getInstance());
109: } else {
110: return SingletonIterator.makeIterator(doc
111: .selectID(idrefs));
112: }
113: } else {
114: SequenceIterator idrefs = argument[0].iterate(context);
115: IdMappingFunction map = new IdMappingFunction();
116: map.document = doc;
117: SequenceIterator result = new MappingIterator(idrefs, map,
118: null);
119: return new DocumentOrderIterator(result, LocalOrderComparer
120: .getInstance());
121: }
122: }
123:
124: private static class IdMappingFunction implements MappingFunction {
125:
126: public DocumentInfo document;
127:
128: /**
129: * Evaluate the function for a single string value
130: * (implements the MappingFunction interface)
131: */
132:
133: public Object map(Item item, XPathContext c)
134: throws XPathException {
135:
136: String idrefs = item.getStringValue().trim();
137:
138: // If this value contains a space, we need to break it up into its
139: // separate tokens; if not, we can process it directly
140:
141: if (idrefs.indexOf(0x20) >= 0 || idrefs.indexOf(0x09) >= 0
142: || idrefs.indexOf(0x0a) >= 0
143: || idrefs.indexOf(0x0d) >= 0) {
144: StringTokenIterator tokens = new StringTokenIterator(
145: idrefs);
146: IdMappingFunction submap = new IdMappingFunction();
147: submap.document = document;
148: return new MappingIterator(tokens, submap, null);
149:
150: } else {
151: return document.selectID(idrefs);
152: }
153: }
154: }
155:
156: }
157:
158: //
159: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
160: // you may not use this file except in compliance with the License. You may obtain a copy of the
161: // License at http://www.mozilla.org/MPL/
162: //
163: // Software distributed under the License is distributed on an "AS IS" basis,
164: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
165: // See the License for the specific language governing rights and limitations under the License.
166: //
167: // The Original Code is: all this file.
168: //
169: // The Initial Developer of the Original Code is Michael H. Kay.
170: //
171: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
172: //
173: // Contributor(s): none.
174: //
|