001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.*;
004: import net.sf.saxon.sort.DocumentOrderIterator;
005: import net.sf.saxon.sort.GlobalOrderComparer;
006: import net.sf.saxon.trace.Location;
007: import net.sf.saxon.trans.DynamicError;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.ItemType;
010: import net.sf.saxon.type.TypeHierarchy;
011: import net.sf.saxon.value.AtomicValue;
012: import net.sf.saxon.value.Cardinality;
013:
014: import java.io.PrintStream;
015: import java.util.Iterator;
016:
017: /**
018: * A simple mapping expression is an expression A/B where B has a static type that is an atomic type.
019: * For example, * / name().
020: */
021:
022: public final class SimpleMappingExpression extends ComputedExpression
023: implements MappingFunction {
024:
025: private Expression start;
026: private Expression step;
027: private boolean isHybrid;
028:
029: /**
030: * Constructor
031: * @param start A node-set expression denoting the absolute or relative set of nodes from which the
032: * navigation path should start.
033: * @param step The step to be followed from each node in the start expression to yield a new
034: * node-set
035: * @param isHybrid if true, indicates that we don't know statically whether the step expression will
036: * return nodes or atomic values. If false, we know it will return atomic values.
037: */
038:
039: public SimpleMappingExpression(Expression start, Expression step,
040: boolean isHybrid) {
041: this .start = start;
042: this .step = step;
043: this .isHybrid = isHybrid;
044: adoptChildExpression(start);
045: adoptChildExpression(step);
046:
047: }
048:
049: /**
050: * Get the start expression (the left-hand operand)
051: */
052:
053: public Expression getStartExpression() {
054: return start;
055: }
056:
057: /**
058: * Get the step expression (the right-hand operand)
059: */
060:
061: public Expression getStepExpression() {
062: return step;
063: }
064:
065: /**
066: * Determine the data type of the items returned by this exprssion
067: * @return the type of the step
068: * @param th
069: */
070:
071: public final ItemType getItemType(TypeHierarchy th) {
072: return step.getItemType(th);
073: }
074:
075: /**
076: * Simplify an expression
077: * @return the simplified expression
078: */
079:
080: public Expression simplify(StaticContext env) {
081: // We rely on the fact that the operands were already simplified
082: return this ;
083: }
084:
085: /**
086: * Type-check the expression
087: */
088:
089: public Expression typeCheck(StaticContext env,
090: ItemType contextItemType) {
091: // rely on the fact that the original path expression has already been type-checked
092: return this ;
093: }
094:
095: public Expression optimize(Optimizer opt, StaticContext env,
096: ItemType contextItemType) throws XPathException {
097: return this ;
098: }
099:
100: /**
101: * Promote this expression if possible
102: */
103:
104: public Expression promote(PromotionOffer offer)
105: throws XPathException {
106: Expression exp = offer.accept(this );
107: if (exp != null) {
108: return exp;
109: } else {
110: start = doPromotion(start, offer);
111: if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
112: || offer.action == PromotionOffer.REPLACE_CURRENT) {
113: // Don't pass on other requests. We could pass them on, but only after augmenting
114: // them to say we are interested in subexpressions that don't depend on either the
115: // outer context or the inner context.
116: step = doPromotion(step, offer);
117: }
118: resetStaticProperties();
119: return this ;
120: }
121: }
122:
123: /**
124: * Get the immediate subexpressions of this expression
125: */
126:
127: public Iterator iterateSubExpressions() {
128: return new PairIterator(start, step);
129: }
130:
131: /**
132: * Determine which aspects of the context the expression depends on. The result is
133: * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
134: * XPathContext.CURRENT_NODE
135: */
136:
137: public int computeDependencies() {
138: return start.getDependencies() |
139: // not all dependencies in the step matter, because the context node, etc,
140: // are not those of the outer expression
141: (step.getDependencies() & StaticProperty.DEPENDS_ON_XSLT_CONTEXT);
142: }
143:
144: /**
145: * Get the static properties of this expression (other than its type). The result is
146: * bit-signficant. These properties are used for optimizations. In general, if
147: * property bit is set, it is true, but if it is unset, the value is unknown.
148: */
149:
150: public int computeSpecialProperties() {
151: int p = super .computeSpecialProperties();
152: if ((start.getSpecialProperties() & step.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
153: p |= StaticProperty.NON_CREATIVE;
154: }
155: return p;
156: }
157:
158: /**
159: * Determine the static cardinality of the expression
160: */
161:
162: public int computeCardinality() {
163: int c1 = start.getCardinality();
164: int c2 = step.getCardinality();
165: return Cardinality.multiply(c1, c2);
166: }
167:
168: /**
169: * Is this expression the same as another expression?
170: */
171:
172: public boolean equals(Object other) {
173: if (!(other instanceof SimpleMappingExpression)) {
174: return false;
175: }
176: SimpleMappingExpression p = (SimpleMappingExpression) other;
177: return (start.equals(p.start) && step.equals(p.step));
178: }
179:
180: /**
181: * get HashCode for comparing two expressions
182: */
183:
184: public int hashCode() {
185: return "SimpleMappingExpression".hashCode() + start.hashCode()
186: + step.hashCode();
187: }
188:
189: /**
190: * Iterate the path-expression in a given context
191: * @param context the evaluation context
192: */
193:
194: public SequenceIterator iterate(XPathContext context)
195: throws XPathException {
196:
197: // This class delivers the result of the path expression in unsorted order,
198: // without removal of duplicates. If sorting and deduplication are needed,
199: // this is achieved by wrapping the path expression in a DocumentSorter
200:
201: SequenceIterator result = start.iterate(context);
202: XPathContext context2 = context.newMinorContext();
203: context2.setCurrentIterator(result);
204: context2.setOriginatingConstructType(Location.PATH_EXPRESSION);
205:
206: result = new MappingIterator(result, this , context2);
207: if (isHybrid) {
208: // This case is rare so we don't worry too much about performance
209: // Peek at the first node, and depending on its type, check that all the items
210: // are atomic values or that all are nodes.
211: Item first = result.next();
212: if (first == null) {
213: return EmptyIterator.getInstance();
214: } else if (first instanceof AtomicValue) {
215: return new MappingIterator(result.getAnother(),
216: AtomicValueChecker.theInstance, null);
217: } else {
218: return new DocumentOrderIterator(new MappingIterator(
219: result.getAnother(), NodeChecker.theInstance,
220: null), GlobalOrderComparer.getInstance());
221: }
222: } else {
223: return result;
224: }
225: }
226:
227: /**
228: * Mapping function, from a node returned by the start iteration, to a sequence
229: * returned by the child.
230: */
231:
232: public Object map(Item item, XPathContext context)
233: throws XPathException {
234: return step.iterate(context);
235: }
236:
237: /**
238: * Diagnostic print of expression structure
239: */
240:
241: public void display(int level, NamePool pool, PrintStream out) {
242: out.println(ExpressionTool.indent(level) + "map /");
243: start.display(level + 1, pool, out);
244: step.display(level + 1, pool, out);
245: }
246:
247: private static class AtomicValueChecker implements MappingFunction {
248: public static AtomicValueChecker theInstance = new AtomicValueChecker();
249:
250: public Object map(Item item, XPathContext context)
251: throws XPathException {
252: if (item instanceof AtomicValue) {
253: return item;
254: } else {
255: DynamicError err = new DynamicError(
256: "Cannot mix nodes and atomic values in the result of a path expression");
257: err.setErrorCode("XPTY0018");
258: err.setXPathContext(context);
259: throw err;
260: }
261: }
262: }
263:
264: private static class NodeChecker implements MappingFunction {
265: public static NodeChecker theInstance = new NodeChecker();
266:
267: public Object map(Item item, XPathContext context)
268: throws XPathException {
269: if (item instanceof NodeInfo) {
270: return item;
271: } else {
272: DynamicError err = new DynamicError(
273: "Cannot mix nodes and atomic values in the result of a path expression");
274: err.setErrorCode("XPTY0018");
275: err.setXPathContext(context);
276: throw err;
277: }
278: }
279: }
280: }
281:
282: //
283: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
284: // you may not use this file except in compliance with the License. You may obtain a copy of the
285: // License at http://www.mozilla.org/MPL/
286: //
287: // Software distributed under the License is distributed on an "AS IS" basis,
288: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
289: // See the License for the specific language governing rights and limitations under the License.
290: //
291: // The Original Code is: all this file.
292: //
293: // The Initial Developer of the Original Code is Michael H. Kay.
294: //
295: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
296: //
297: // Contributor(s): none.
298: //
|