001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Item;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.om.SequenceIterator;
006: import net.sf.saxon.trans.StaticError;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.type.AnyItemType;
009: import net.sf.saxon.type.ItemType;
010: import net.sf.saxon.type.Type;
011: import net.sf.saxon.type.TypeHierarchy;
012: import net.sf.saxon.value.Value;
013: import net.sf.saxon.value.Cardinality;
014: import net.sf.saxon.event.SequenceReceiver;
015: import net.sf.saxon.event.TypeCheckingFilter;
016: import net.sf.saxon.pattern.DocumentNodeTest;
017:
018: /**
019: * A ItemChecker implements the item type checking of "treat as": that is,
020: * it returns the supplied sequence, checking that all its items are of the correct type
021: */
022:
023: public final class ItemChecker extends UnaryExpression {
024:
025: private ItemType requiredItemType;
026: private RoleLocator role;
027:
028: /**
029: * Constructor
030: */
031:
032: public ItemChecker(Expression sequence, ItemType itemType,
033: RoleLocator role) {
034: super (sequence);
035: this .requiredItemType = itemType;
036: this .role = role;
037: adoptChildExpression(sequence);
038: }
039:
040: /**
041: * Get the required type
042: */
043:
044: public ItemType getRequiredType() {
045: return requiredItemType;
046: }
047:
048: /**
049: * Simplify an expression
050: */
051:
052: public Expression simplify(StaticContext env) throws XPathException {
053: operand = operand.simplify(env);
054: if (requiredItemType instanceof AnyItemType) {
055: return operand;
056: }
057: return this ;
058: }
059:
060: /**
061: * Type-check the expression
062: */
063:
064: public Expression typeCheck(StaticContext env,
065: ItemType contextItemType) throws XPathException {
066: operand = operand.typeCheck(env, contextItemType);
067: // When analyze is called a second time, we might have more information...
068:
069: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
070: int card = operand.getCardinality();
071: if (card == StaticProperty.EMPTY) {
072: //value is always empty, so no item checking needed
073: return operand;
074: }
075: ItemType supplied = operand.getItemType(th);
076: int relation = th.relationship(requiredItemType, supplied);
077: if (relation == TypeHierarchy.SAME_TYPE
078: || relation == TypeHierarchy.SUBSUMES) {
079: return operand;
080: }
081: if (relation == TypeHierarchy.DISJOINT) {
082: if (Cardinality.allowsZero(card)) {
083: String message = role.composeErrorMessage(
084: requiredItemType, operand.getItemType(th), env
085: .getNamePool());
086: env.issueWarning(
087: "Warning: the only value that can pass type-checking is an empty sequence. "
088: + message, this );
089: } else {
090: String message = role.composeErrorMessage(
091: requiredItemType, operand.getItemType(th), env
092: .getNamePool());
093: StaticError err = new StaticError(message);
094: err.setErrorCode(role.getErrorCode());
095: err.setLocator(this );
096: err.setIsTypeError(true);
097: throw err;
098: }
099: }
100: return this ;
101: }
102:
103: /**
104: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
105: * This method indicates which of these methods is provided. This implementation provides both iterate() and
106: * process() methods natively.
107: */
108:
109: public int getImplementationMethod() {
110: int m = ITERATE_METHOD | PROCESS_METHOD;
111: if (!Cardinality.allowsMany(getCardinality())) {
112: m |= EVALUATE_METHOD;
113: }
114: return m;
115: }
116:
117: /**
118: * Iterate over the sequence of values
119: */
120:
121: public SequenceIterator iterate(XPathContext context)
122: throws XPathException {
123: SequenceIterator base = operand.iterate(context);
124: ItemCheckMappingFunction map = new ItemCheckMappingFunction();
125: map.externalContext = context;
126: return new MappingIterator(base, map, null);
127: }
128:
129: /**
130: * Mapping function: this is used only if the expression does not allow a sequence of more than
131: * one item.
132: */
133: private class ItemCheckMappingFunction implements MappingFunction {
134: public XPathContext externalContext;
135:
136: public Object map(Item item, XPathContext nullcontext)
137: throws XPathException {
138: testConformance(item, externalContext);
139: return item;
140: }
141: }
142:
143: /**
144: * Evaluate as an Item.
145: */
146:
147: public Item evaluateItem(XPathContext context)
148: throws XPathException {
149: Item item = operand.evaluateItem(context);
150: if (item == null)
151: return null;
152: testConformance(item, context);
153: return item;
154: }
155:
156: /**
157: * Process the instruction, without returning any tail calls
158: *
159: * @param context The dynamic context, giving access to the current node,
160: * the current variables, etc.
161: */
162:
163: public void process(XPathContext context) throws XPathException {
164: Expression next = operand;
165: int card = StaticProperty.ALLOWS_ZERO_OR_MORE;
166: if (next instanceof CardinalityChecker) {
167: card = ((CardinalityChecker) next).getRequiredCardinality();
168: next = ((CardinalityChecker) next).getBaseExpression();
169: }
170: if ((next.getImplementationMethod() & PROCESS_METHOD) != 0
171: && !(requiredItemType instanceof DocumentNodeTest)) {
172: SequenceReceiver out = context.getReceiver();
173: TypeCheckingFilter filter = new TypeCheckingFilter();
174: filter.setUnderlyingReceiver(out);
175: filter.setPipelineConfiguration(out
176: .getPipelineConfiguration());
177: filter.setRequiredType(requiredItemType, card, role);
178: context.setReceiver(filter);
179: next.process(context);
180: filter.close();
181: context.setReceiver(filter);
182: } else {
183: super .process(context);
184: }
185: }
186:
187: private void testConformance(Item item, XPathContext context)
188: throws XPathException {
189: if (!requiredItemType.matchesItem(item, context)) {
190: String message;
191: if (context == null) {
192: // no name pool available
193: message = "Supplied value of type "
194: + Type.displayTypeName(item)
195: + " does not match the required type of "
196: + role.getMessage();
197: } else {
198: final NamePool pool = context.getNamePool();
199: final TypeHierarchy th = pool.getTypeHierarchy();
200: message = role.composeErrorMessage(requiredItemType,
201: Value.asValue(item).getItemType(th), pool);
202: }
203: String errorCode = role.getErrorCode();
204: if ("XPDY0050".equals(errorCode)) {
205: // error in "treat as" assertion
206: dynamicError(message, errorCode, context);
207: } else {
208: typeError(message, errorCode, context);
209: }
210: }
211: }
212:
213: /**
214: * Determine the data type of the items returned by the expression
215: * @param th
216: */
217:
218: public ItemType getItemType(TypeHierarchy th) {
219: // TODO: take the intersection of the required type with the static type of the operand
220: return requiredItemType;
221: }
222:
223: /**
224: * Is this expression the same as another expression?
225: */
226:
227: public boolean equals(Object other) {
228: return super .equals(other)
229: && requiredItemType == ((ItemChecker) other).requiredItemType;
230: }
231:
232: /**
233: * Give a string representation of the operator for use in diagnostics
234: * @return the operator, as a string
235: */
236:
237: protected String displayOperator(NamePool pool) {
238: return "treat as " + requiredItemType.toString(pool);
239: }
240:
241: }
242:
243: //
244: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
245: // you may not use this file except in compliance with the License. You may obtain a copy of the
246: // License at http://www.mozilla.org/MPL/
247: //
248: // Software distributed under the License is distributed on an "AS IS" basis,
249: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
250: // See the License for the specific language governing rights and limitations under the License.
251: //
252: // The Original Code is: all this file.
253: //
254: // The Initial Developer of the Original Code is Michael H. Kay
255: //
256: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
257: //
258: // Contributor(s): none.
259: //
|