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.sort.IntHashMap;
006: import net.sf.saxon.trans.DynamicError;
007: import net.sf.saxon.trans.StaticError;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.*;
010: import net.sf.saxon.value.*;
011:
012: /**
013: * Cast Expression: implements "cast as data-type ( expression )". It also allows an internal
014: * cast, which has the same semantics as a user-requested cast, but maps an empty sequence to
015: * an empty sequence.
016: */
017:
018: public final class CastExpression extends UnaryExpression {
019:
020: static IntHashMap castingTable = new IntHashMap(25);
021:
022: static void addAllowedCasts(int source, int[] target) {
023: castingTable.put(source, target);
024: }
025:
026: /**
027: * The following data represents all the "Y" and "M" entries in section 17.1 of the F+O spec
028: */
029:
030: static {
031: final int uat = Type.UNTYPED_ATOMIC;
032: final int str = Type.STRING;
033: final int flt = Type.FLOAT;
034: final int dbl = Type.DOUBLE;
035: final int dec = Type.DECIMAL;
036: final int ing = Type.INTEGER;
037: final int dur = Type.DURATION;
038: final int ymd = Type.YEAR_MONTH_DURATION;
039: final int dtd = Type.DAY_TIME_DURATION;
040: final int dtm = Type.DATE_TIME;
041: final int tim = Type.TIME;
042: final int dat = Type.DATE;
043: final int gym = Type.G_YEAR_MONTH;
044: final int gyr = Type.G_YEAR;
045: final int gmd = Type.G_MONTH_DAY;
046: final int gdy = Type.G_DAY;
047: final int gmo = Type.G_MONTH;
048: final int boo = Type.BOOLEAN;
049: final int b64 = Type.BASE64_BINARY;
050: final int hxb = Type.HEX_BINARY;
051: final int uri = Type.ANY_URI;
052: final int qnm = Type.QNAME;
053: final int not = Type.NOTATION;
054:
055: final int[] t01 = { uat, str, flt, dbl, dec, ing, dur, ymd,
056: dtd, dtm, tim, dat, gym, gyr, gmd, gdy, gmo, boo, b64,
057: hxb, uri };
058: addAllowedCasts(uat, t01);
059: final int[] t02 = { uat, str, flt, dbl, dec, ing, dur, ymd,
060: dtd, dtm, tim, dat, gym, gyr, gmd, gdy, gmo, boo, b64,
061: hxb, uri, qnm, not };
062: addAllowedCasts(str, t02);
063: final int[] t03 = { uat, str, flt, dbl, dec, ing, boo };
064: addAllowedCasts(flt, t03);
065: addAllowedCasts(dbl, t03);
066: addAllowedCasts(dec, t03);
067: addAllowedCasts(ing, t03);
068: final int[] t04 = { uat, str, dur, ymd, dtd };
069: addAllowedCasts(dur, t04);
070: addAllowedCasts(ymd, t04);
071: addAllowedCasts(dtd, t04);
072: final int[] t05 = { uat, str, dtm, tim, dat, gym, gyr, gmd,
073: gdy, gmo };
074: addAllowedCasts(dtm, t05);
075: final int[] t06 = { uat, str, tim };
076: addAllowedCasts(tim, t06);
077: final int[] t07 = { uat, str, dtm, dat, gym, gyr, gmd, gdy, gmo };
078: addAllowedCasts(dat, t07);
079: final int[] t08 = { uat, str, gym };
080: addAllowedCasts(gym, t08);
081: final int[] t09 = { uat, str, gyr };
082: addAllowedCasts(gyr, t09);
083: final int[] t10 = { uat, str, gmd };
084: addAllowedCasts(gmd, t10);
085: final int[] t11 = { uat, str, gdy };
086: addAllowedCasts(gdy, t11);
087: final int[] t12 = { uat, str, gmo };
088: addAllowedCasts(gmo, t12);
089: final int[] t13 = { uat, str, flt, dbl, dec, ing, boo };
090: addAllowedCasts(boo, t13);
091: final int[] t14 = { uat, str, b64, hxb };
092: addAllowedCasts(b64, t14);
093: addAllowedCasts(hxb, t14);
094: final int[] t15 = { uat, str, uri };
095: addAllowedCasts(uri, t15);
096: final int[] t16 = { uat, str, qnm };
097: addAllowedCasts(qnm, t16);
098: final int[] t17 = { uat, str, not };
099: addAllowedCasts(not, t17);
100: }
101:
102: /**
103: * Determine whether casting from a source type to a target type is possible
104: * @param source a primitive type (one that has an entry in the casting table)
105: * @param target another primitive type
106: * @return true if the entry in the casting table is either "Y" (casting always succeeds)
107: * or "M" (casting allowed but may fail for some values)
108: */
109:
110: public static boolean isPossibleCast(int source, int target) {
111: if (source == Type.ANY_ATOMIC || source == Type.EMPTY) {
112: return true;
113: }
114: int[] targets = (int[]) castingTable.get(source);
115: if (targets == null) {
116: return false;
117: }
118: for (int i = 0; i < targets.length; i++) {
119: if (targets[i] == target) {
120: return true;
121: }
122: }
123: return false;
124: }
125:
126: private AtomicType targetType;
127: private AtomicType targetPrimitiveType;
128: private boolean allowEmpty = false;
129: private boolean derived = false;
130:
131: public CastExpression(Expression source, AtomicType target,
132: boolean allowEmpty) {
133: super (source);
134: this .allowEmpty = allowEmpty;
135: targetType = target;
136: targetPrimitiveType = (AtomicType) target
137: .getPrimitiveItemType();
138: derived = (targetType.getFingerprint() != targetPrimitiveType
139: .getFingerprint());
140: adoptChildExpression(source);
141: }
142:
143: /**
144: * Handle a cast to QName or NOTATION. The argument must be a string literal.
145: */
146:
147: public AtomicValue doQNameCast(StaticContext env)
148: throws XPathException {
149: if (!(operand instanceof StringValue)) {
150: throw new StaticError(
151: "The argument of a QName or NOTATION constructor must be a string literal");
152: }
153: return QNameValue.castToQName((StringValue) operand,
154: targetType, env);
155: }
156:
157: /**
158: * Simplify the expression
159: * @return the simplified expression
160: */
161:
162: public Expression simplify(StaticContext env) throws XPathException {
163: if ((targetType instanceof BuiltInAtomicType)
164: && !env.isAllowedBuiltInType(targetType)) {
165: // this is checked here because the ConstructorFunctionLibrary doesn't have access to the static
166: // context at bind time
167: StaticError err = new StaticError("The type "
168: + targetType.getDisplayName()
169: + " is not recognized by a Basic XSLT Processor",
170: this );
171: err.setErrorCode("XPST0080");
172: throw err;
173: }
174: operand = operand.simplify(env);
175: if (operand instanceof AtomicValue) {
176: return typeCheck(env, Type.ITEM_TYPE);
177: }
178: return this ;
179: }
180:
181: /**
182: * Type-check the expression
183: */
184:
185: public Expression typeCheck(StaticContext env,
186: ItemType contextItemType) throws XPathException {
187: operand = operand.typeCheck(env, contextItemType);
188: SequenceType atomicType = SequenceType.makeSequenceType(
189: Type.ANY_ATOMIC_TYPE, getCardinality());
190:
191: RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP,
192: "cast as", 0, null);
193: role.setSourceLocator(this );
194: operand = TypeChecker.staticTypeCheck(operand, atomicType,
195: false, role, env);
196:
197: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
198: ItemType sourceType = operand.getItemType(th);
199: if (th.isSubType(sourceType, targetType)) {
200: return operand;
201: // It's not entirely clear that the spec permits this. Perhaps we should change the type label?
202: // On the other hand, it's generally true that any expression defined to return an X
203: // is allowed to return a subtype of X.
204: }
205: if (targetType.isNamespaceSensitive()) {
206: return new CastAsQName(operand, targetType).analyze(env,
207: contextItemType);
208: }
209: if (operand instanceof AtomicValue) {
210: return (AtomicValue) evaluateItem(env
211: .makeEarlyEvaluationContext());
212: }
213: if (operand instanceof EmptySequence) {
214: if (allowEmpty) {
215: return operand;
216: } else {
217: StaticError err = new StaticError(
218: "Cast can never succeed: the operand must not be an empty sequence");
219: err.setErrorCode("XPTY0004");
220: err.setIsTypeError(true);
221: throw err;
222: }
223: }
224: int p = sourceType.getPrimitiveType();
225: if (!isPossibleCast(p, targetType.getPrimitiveType())) {
226: StaticError err = new StaticError("Casting from "
227: + sourceType + " to " + targetType
228: + " can never succeed");
229: err.setErrorCode("XPTY0004");
230: err.setIsTypeError(true);
231: throw err;
232: }
233:
234: return this ;
235: }
236:
237: /**
238: * Get the static cardinality of the expression
239: */
240:
241: public int computeCardinality() {
242: return (allowEmpty ? StaticProperty.ALLOWS_ZERO_OR_ONE
243: : StaticProperty.EXACTLY_ONE);
244: }
245:
246: /**
247: * Get the static type of the expression
248: * @param th
249: */
250:
251: public ItemType getItemType(TypeHierarchy th) {
252: return targetType;
253: }
254:
255: /**
256: * Determine the special properties of this expression
257: * @return {@link StaticProperty#NON_CREATIVE}.
258: */
259:
260: public int computeSpecialProperties() {
261: int p = super .computeSpecialProperties();
262: return p | StaticProperty.NON_CREATIVE;
263: }
264:
265: /**
266: * Evaluate the expression
267: */
268:
269: public Item evaluateItem(XPathContext context)
270: throws XPathException {
271: AtomicValue value = (AtomicValue) operand.evaluateItem(context);
272: if (value == null) {
273: if (allowEmpty) {
274: return null;
275: } else {
276: DynamicError e = new DynamicError(
277: "Cast does not allow an empty sequence");
278: e.setXPathContext(context);
279: throw e;
280: }
281: }
282: AtomicValue result = value.convert(targetPrimitiveType,
283: context, true);
284: if (result instanceof ValidationErrorValue) {
285: XPathException err = ((ValidationErrorValue) result)
286: .getException();
287: String code = err.getErrorCodeLocalPart();
288: dynamicError(err.getMessage(), code, context);
289: }
290: if (derived) {
291: result = result.convert(targetType, context, true);
292: if (result instanceof ValidationErrorValue) {
293: XPathException err = ((ValidationErrorValue) result)
294: .getException();
295: String code = err.getErrorCodeLocalPart();
296: dynamicError(err.getMessage(), code, context);
297: }
298: }
299: return result;
300: }
301:
302: /**
303: * Is this expression the same as another expression?
304: */
305:
306: public boolean equals(Object other) {
307: return super .equals(other)
308: && targetType == ((CastExpression) other).targetType
309: && allowEmpty == ((CastExpression) other).allowEmpty;
310: }
311:
312: /**
313: * Give a string representation of the operator for use in diagnostics
314: * @return the operator, as a string
315: */
316:
317: protected String displayOperator(NamePool pool) {
318: return "cast as " + targetType.toString(pool);
319: }
320: }
321:
322: //
323: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
324: // you may not use this file except in compliance with the License. You may obtain a copy of the
325: // License at http://www.mozilla.org/MPL/
326: //
327: // Software distributed under the License is distributed on an "AS IS" basis,
328: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
329: // See the License for the specific language governing rights and limitations under the License.
330: //
331: // The Original Code is: all this file.
332: //
333: // The Initial Developer of the Original Code is Michael H. Kay
334: //
335: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
336: //
337: // Contributor(s): none.
338: //
|