001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.event.SequenceReceiver;
004: import net.sf.saxon.event.TypeCheckingFilter;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.pattern.DocumentNodeTest;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.type.ItemType;
009: import net.sf.saxon.type.Type;
010: import net.sf.saxon.type.TypeHierarchy;
011: import net.sf.saxon.value.Cardinality;
012:
013: /**
014: * A CardinalityChecker implements the cardinality checking of "treat as": that is,
015: * it returns the supplied sequence, checking that its cardinality is correct
016: */
017:
018: public final class CardinalityChecker extends UnaryExpression {
019:
020: private int requiredCardinality = -1;
021: private RoleLocator role;
022:
023: /**
024: * Private Constructor: use factory method
025: */
026:
027: private CardinalityChecker(Expression sequence, int cardinality,
028: RoleLocator role) {
029: super (sequence);
030: this .requiredCardinality = cardinality;
031: this .role = role;
032: computeStaticProperties();
033: adoptChildExpression(sequence);
034: }
035:
036: /**
037: * Factory method to construct a CardinalityChecker. The method may create an expression that combines
038: * the cardinality checking with the functionality of the underlying expression class
039: * @param sequence
040: * @param cardinality
041: * @param role
042: * @return
043: */
044:
045: public static ComputedExpression makeCardinalityChecker(
046: Expression sequence, int cardinality, RoleLocator role) {
047: if (sequence instanceof Atomizer
048: && !Cardinality.allowsMany(cardinality)) {
049: Expression base = ((Atomizer) sequence).getBaseExpression();
050: if (base instanceof ComputedExpression) {
051: ((ComputedExpression) base)
052: .setParentExpression(sequence
053: .getParentExpression());
054: }
055: return new SingletonAtomizer(base, role, Cardinality
056: .allowsZero(cardinality));
057: } else {
058: return new CardinalityChecker(sequence, cardinality, role);
059: }
060: }
061:
062: /**
063: * Get the required cardinality
064: */
065:
066: public int getRequiredCardinality() {
067: return requiredCardinality;
068: }
069:
070: /**
071: * Type-check the expression
072: */
073:
074: public Expression typeCheck(StaticContext env,
075: ItemType contextItemType) throws XPathException {
076: operand = operand.typeCheck(env, contextItemType);
077: if (requiredCardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
078: return operand;
079: }
080: if (Cardinality.subsumes(requiredCardinality, operand
081: .getCardinality())) {
082: return operand;
083: }
084: return this ;
085: }
086:
087: /**
088: * Perform optimisation of an expression and its subexpressions.
089: * <p/>
090: * <p>This method is called after all references to functions and variables have been resolved
091: * to the declaration of the function or variable, and after all type checking has been done.</p>
092: *
093: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
094: * different optimization strategies to be used in different circumstances.
095: * @param env the static context of the expression
096: * @param contextItemType the static type of "." at the point where this expression is invoked.
097: * The parameter is set to null if it is known statically that the context item will be undefined.
098: * If the type of the context item is not known statically, the argument is set to
099: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
100: * @return the original expression, rewritten if appropriate to optimize execution
101: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
102: * (typically a type error)
103: */
104:
105: public Expression optimize(Optimizer opt, StaticContext env,
106: ItemType contextItemType) throws XPathException {
107: operand = operand.optimize(opt, env, contextItemType);
108: if (requiredCardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
109: return operand;
110: }
111: if (Cardinality.subsumes(requiredCardinality, operand
112: .getCardinality())) {
113: return operand;
114: }
115: return this ;
116: }
117:
118: /**
119: * Set the error code to be returned (this is used when evaluating the functions such
120: * as exactly-one() which have their own error codes)
121: */
122:
123: public void setErrorCode(String code) {
124: role.setErrorCode(code);
125: }
126:
127: /**
128: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
129: * This method indicates which of these methods is provided. This implementation provides both iterate() and
130: * process() methods natively.
131: */
132:
133: public int getImplementationMethod() {
134: int m = ITERATE_METHOD | PROCESS_METHOD;
135: if (!Cardinality.allowsMany(requiredCardinality)) {
136: m |= EVALUATE_METHOD;
137: }
138: return m;
139: }
140:
141: /**
142: * Iterate over the sequence of values
143: */
144:
145: public SequenceIterator iterate(XPathContext context)
146: throws XPathException {
147: SequenceIterator base = operand.iterate(context);
148:
149: // If the base iterator knows how many items there are, then check it now rather than wasting time
150:
151: if ((base.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
152: int count = ((LastPositionFinder) base).getLastPosition();
153: if (count == 0
154: && !Cardinality.allowsZero(requiredCardinality)) {
155: typeError("An empty sequence is not allowed as the "
156: + role.getMessage(), role.getErrorCode(),
157: context);
158: }
159: if (count > 1
160: && !Cardinality.allowsMany(requiredCardinality)) {
161: typeError(
162: "A sequence of more than one item is not allowed as the "
163: + role.getMessage(), role
164: .getErrorCode(), context);
165: }
166: return base;
167: }
168:
169: // Otherwise return an iterator that does the checking on the fly
170:
171: if (!Cardinality.allowsZero(requiredCardinality)) {
172: // To check for an empty sequence, we using a ClosingIterator which causes a close()
173: // method to be called after the last item has been read
174: final XPathContext callingContext = context;
175: ClosingAction onClose = new ClosingAction() {
176: public void close(SequenceIterator base, int count)
177: throws XPathException {
178: if (count == 0) {
179: typeError(
180: "An empty sequence is not allowed as the "
181: + role.getMessage(), role
182: .getErrorCode(), callingContext);
183: }
184: }
185: };
186: base = new ClosingIterator(base, onClose);
187: }
188: if (!Cardinality.allowsMany(requiredCardinality)) {
189: CardinalityCheckingFunction map = new CardinalityCheckingFunction();
190: map.iterator = base;
191: base = new MappingIterator(base, map, null);
192: }
193: return base;
194: }
195:
196: /**
197: * Mapping function used to check for sequences of length > 1 when this is not permitted
198: */
199:
200: private class CardinalityCheckingFunction implements
201: MappingFunction {
202:
203: public SequenceIterator iterator;
204:
205: public Object map(Item item, XPathContext context)
206: throws XPathException {
207: if (iterator.position() == 2) {
208: typeError(
209: "A sequence of more than one item is not allowed as the "
210: + role.getMessage(), role
211: .getErrorCode(), context);
212: return null;
213: }
214: return item;
215: }
216: }
217:
218: /**
219: * Evaluate as an Item.
220: */
221:
222: public Item evaluateItem(XPathContext context)
223: throws XPathException {
224: SequenceIterator iter = operand.iterate(context);
225: Item item = null;
226: while (true) {
227: Item nextItem = iter.next();
228: if (nextItem == null)
229: break;
230: if (item != null) {
231: typeError(
232: "A sequence of more than one item is not allowed as the "
233: + role.getMessage(), role
234: .getErrorCode(), context);
235: return null;
236: }
237: item = nextItem;
238: }
239: if (item == null
240: && !Cardinality.allowsZero(requiredCardinality)) {
241: typeError("An empty sequence is not allowed as the "
242: + role.getMessage(), role.getErrorCode(), context);
243: return null;
244: }
245: return item;
246: }
247:
248: /**
249: * Process the instruction, without returning any tail calls
250: *
251: * @param context The dynamic context, giving access to the current node,
252: * the current variables, etc.
253: */
254:
255: public void process(XPathContext context) throws XPathException {
256: Expression next = operand;
257: ItemType type = Type.ITEM_TYPE;
258: if (next instanceof ItemChecker) {
259: type = ((ItemChecker) next).getRequiredType();
260: next = ((ItemChecker) next).getBaseExpression();
261: }
262: if ((next.getImplementationMethod() & PROCESS_METHOD) != 0
263: && !(type instanceof DocumentNodeTest)) {
264: SequenceReceiver out = context.getReceiver();
265: TypeCheckingFilter filter = new TypeCheckingFilter();
266: filter.setUnderlyingReceiver(out);
267: filter.setPipelineConfiguration(out
268: .getPipelineConfiguration());
269: filter.setRequiredType(type, requiredCardinality, role);
270: context.setReceiver(filter);
271: next.process(context);
272: filter.close();
273: context.setReceiver(out);
274: } else {
275: super .process(context);
276: }
277: }
278:
279: /**
280: * Determine the data type of the items returned by the expression, if possible
281: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
282: * or Type.ITEM (meaning not known in advance)
283: * @param th
284: */
285:
286: public ItemType getItemType(TypeHierarchy th) {
287: return operand.getItemType(th);
288: }
289:
290: /**
291: * Determine the static cardinality of the expression
292: */
293:
294: public int computeCardinality() {
295: return requiredCardinality;
296: }
297:
298: /**
299: * Get the static properties of this expression (other than its type). The result is
300: * bit-signficant. These properties are used for optimizations. In general, if
301: * property bit is set, it is true, but if it is unset, the value is unknown.
302: */
303:
304: public int computeSpecialProperties() {
305: return operand.getSpecialProperties();
306: }
307:
308: /**
309: * Is this expression the same as another expression?
310: */
311:
312: public boolean equals(Object other) {
313: return super .equals(other)
314: && this .requiredCardinality == ((CardinalityChecker) other).requiredCardinality;
315: }
316:
317: /**
318: * Diagnostic print of expression structure
319: */
320:
321: public String displayOperator(NamePool pool) {
322: return "checkCardinality ("
323: + Cardinality.toString(requiredCardinality) + ')';
324: }
325:
326: }
327:
328: //
329: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
330: // you may not use this file except in compliance with the License. You may obtain a copy of the
331: // License at http://www.mozilla.org/MPL/
332: //
333: // Software distributed under the License is distributed on an "AS IS" basis,
334: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
335: // See the License for the specific language governing rights and limitations under the License.
336: //
337: // The Original Code is: all this file.
338: //
339: // The Initial Developer of the Original Code is Michael H. Kay
340: //
341: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
342: //
343: // Contributor(s): none.
344: //
|