KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > expr > TypeChecker


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.functions.NumberFn;
4 import net.sf.saxon.functions.StringFn;
5 import net.sf.saxon.functions.SystemFunction;
6 import net.sf.saxon.style.StandardNames;
7 import net.sf.saxon.trans.StaticError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.trans.DynamicError;
10 import net.sf.saxon.type.AnyItemType;
11 import net.sf.saxon.type.AtomicType;
12 import net.sf.saxon.type.ItemType;
13 import net.sf.saxon.type.Type;
14 import net.sf.saxon.value.*;
15 import net.sf.saxon.pattern.NoNodeTest;
16
17 import javax.xml.transform.SourceLocator JavaDoc;
18
19 /**
20  * This class provides Saxon's type checking capability. It contains a static method,
21  * staticTypeCheck, which is called at compile time to perform type checking of
22  * an expression. This class is never instantiated.
23  */

24
25 public final class TypeChecker {
26
27     // Class is not instantiated
28
private TypeChecker() {}
29
30     /**
31      * Check an expression against a required type, modifying it if necessary.
32      *
33      * <p>This method takes the supplied expression and checks to see whether it is
34      * known statically to conform to the specified type. There are three possible
35      * outcomes. If the static type of the expression is a subtype of the required
36      * type, the method returns the expression unchanged. If the static type of
37      * the expression is incompatible with the required type (for example, if the
38      * supplied type is integer and the required type is string) the method throws
39      * an exception (this results in a compile-time type error being reported). If
40      * the static type is a supertype of the required type, then a new expression
41      * is constructed that evaluates the original expression and checks the dynamic
42      * type of the result; this new expression is returned as the result of the
43      * method.</p>
44      *
45      * <p>The rules applied are those for function calling in XPath, that is, the rules
46      * that the argument of a function call must obey in relation to the signature of
47      * the function. Some contexts require slightly different rules (for example,
48      * operands of polymorphic operators such as "+"). In such cases this method cannot
49      * be used.</p>
50      *
51      * <p>Note that this method does <b>not</b> do recursive type-checking of the
52      * sub-expressions.</p>
53      *
54      * @param supplied The expression to be type-checked
55      * @param req The required type for the context in which the expression is used
56      * @param backwardsCompatible
57      * True if XPath 1.0 backwards compatibility mode is applicable
58      * @param role Information about the role of the subexpression within the
59      * containing expression, used to provide useful error messages
60      * @param env The static context containing the types being checked. At present
61      * this is used only to locate a NamePool
62      * @return The original expression if it is type-safe, or the expression
63      * wrapped in a run-time type checking expression if not.
64      * @throws net.sf.saxon.trans.StaticError if the supplied type is statically inconsistent with the
65      * required type (that is, if they have no common subtype)
66      */

67
68     public static Expression staticTypeCheck(Expression supplied,
69                                              SequenceType req,
70                                              boolean backwardsCompatible,
71                                              RoleLocator role,
72                                              StaticContext env)
73     throws StaticError {
74
75         // System.err.println("Static Type Check on expression (requiredType = " + req + "):"); supplied.display(10);
76

77         Expression exp = supplied;
78
79         ItemType reqItemType = req.getPrimaryType();
80         int reqCard = req.getCardinality();
81         boolean allowsMany = Cardinality.allowsMany(reqCard);
82
83         ItemType suppliedItemType = null;
84             // item type of the supplied expression: null means not yet calculated
85
int suppliedCard = -1;
86             // cardinality of the supplied expression: -1 means not yet calculated
87

88         boolean cardOK = (reqCard == StaticProperty.ALLOWS_ZERO_OR_MORE);
89         // Unless the required cardinality is zero-or-more (no constraints).
90
// check the static cardinality of the supplied expression
91
if (!cardOK) {
92             suppliedCard = exp.getCardinality();
93             cardOK = Cardinality.subsumes(reqCard, suppliedCard);
94                 // May later find that cardinality is not OK after all, if atomization takes place
95
}
96
97         boolean itemTypeOK = reqItemType instanceof AnyItemType;
98         // Unless the required item type and content type are ITEM (no constraints)
99
// check the static item type against the supplied expression.
100
// NOTE: we don't currently do any static inference regarding the content type
101
if (!itemTypeOK) {
102             suppliedItemType = exp.getItemType();
103             if (suppliedItemType instanceof NoNodeTest) {
104                 // supplied type is empty(): this can violate a cardinality constraint but not an item type constraint
105
itemTypeOK = true;
106             } else {
107                 int relation = Type.relationship(reqItemType, suppliedItemType);
108                 itemTypeOK = relation == Type.SAME_TYPE || relation == Type.SUBSUMES;
109             }
110         }
111
112
113         // Handle the special rules for 1.0 compatibility mode
114
if (backwardsCompatible && !allowsMany) {
115             // rule 1
116
if (Cardinality.allowsMany(suppliedCard)) {
117                 ComputedExpression cexp = new FirstItemExpression(exp);
118                 cexp.adoptChildExpression(exp);
119                 exp = cexp;
120                 suppliedCard = StaticProperty.ALLOWS_ZERO_OR_ONE;
121                 cardOK = Cardinality.subsumes(reqCard, suppliedCard);
122             }
123             if (!itemTypeOK) {
124                 // rule 2
125
if (reqItemType == Type.STRING_TYPE) {
126                     StringFn fn = (StringFn)SystemFunction.makeSystemFunction("string", 1, env.getNamePool());
127                     fn.setParentExpression(exp.getParentExpression());
128                     Expression[] args = {exp};
129                     fn.setArguments(args);
130                     try {
131                         exp = fn.simplify(env).typeCheck(env, AnyItemType.getInstance());
132                     } catch (XPathException err) {
133                         throw err.makeStatic();
134                     }
135                     suppliedItemType = Type.STRING_TYPE;
136                     suppliedCard = StaticProperty.EXACTLY_ONE;
137                     cardOK = Cardinality.subsumes(reqCard, suppliedCard);
138                     itemTypeOK = true;
139                 }
140                 // rule 3
141
if (reqItemType == Type.NUMBER_TYPE || reqItemType == Type.DOUBLE_TYPE) {
142                     NumberFn fn = (NumberFn)SystemFunction.makeSystemFunction("number", 1, env.getNamePool());
143                     Expression[] args = {exp};
144                     fn.setArguments(args);
145                     try {
146                         exp = fn.simplify(env).typeCheck(env, AnyItemType.getInstance());
147                     } catch (XPathException err) {
148                         throw err.makeStatic();
149                     }
150                     suppliedItemType = Type.DOUBLE_TYPE;
151                     suppliedCard = StaticProperty.EXACTLY_ONE;
152                     cardOK = Cardinality.subsumes(reqCard, suppliedCard);
153                     itemTypeOK = true;
154                 }
155             }
156         }
157
158         if (!itemTypeOK) {
159             // Now apply the conversions needed in 2.0 mode
160

161             if (reqItemType instanceof AtomicType) {
162
163                 // rule 1: Atomize
164
if (!(suppliedItemType instanceof AtomicType) &&
165                         !(suppliedCard == StaticProperty.EMPTY)) {
166                     ComputedExpression cexp = new Atomizer(exp, env.getConfiguration());
167                     exp = cexp;
168                     suppliedItemType = exp.getItemType();
169                     suppliedCard = exp.getCardinality();
170                     cardOK = Cardinality.subsumes(reqCard, suppliedCard);
171                 }
172
173                 // rule 2: convert untypedAtomic to the required type
174

175                 // 2a: all supplied values are untyped atomic. Convert if necessary, and we're finished.
176

177                 if ((suppliedItemType == Type.UNTYPED_ATOMIC_TYPE)
178                         && !(reqItemType == Type.UNTYPED_ATOMIC_TYPE || reqItemType == Type.ANY_ATOMIC_TYPE)) {
179
180                     ComputedExpression cexp = new UntypedAtomicConverter(exp, (AtomicType)reqItemType, true);
181                     try {
182                         if (exp instanceof Value) {
183                             exp = new SequenceExtent(cexp.iterate(null)).simplify();
184                         } else {
185                             exp = cexp;
186                         }
187                     } catch (XPathException err) {
188                         throw err.makeStatic();
189                     }
190                     itemTypeOK = true;
191                     suppliedItemType = reqItemType;
192                 }
193
194                 // 2b: some supplied values are untyped atomic. Convert these to the required type; but
195
// there may be other values in the sequence that won't convert and still need to be checked
196

197                 if ((suppliedItemType == Type.ANY_ATOMIC_TYPE)
198                     && !(reqItemType == Type.UNTYPED_ATOMIC_TYPE || reqItemType == Type.ANY_ATOMIC_TYPE)) {
199
200                     ComputedExpression cexp = new UntypedAtomicConverter(exp, (AtomicType)reqItemType, false);
201                     try {
202                         if (exp instanceof Value) {
203                             exp = new SequenceExtent(cexp.iterate(null)).simplify();
204                         } else {
205                             exp = cexp;
206                         }
207                     } catch (XPathException err) {
208                         throw err.makeStatic();
209                     }
210                 }
211
212                 // Rule 3a: numeric promotion decimal -> float -> double
213

214                 int rt = ((AtomicType)reqItemType).getFingerprint();
215                 if (rt == StandardNames.XS_DOUBLE || rt == StandardNames.XS_FLOAT) {
216                     if (Type.relationship(suppliedItemType, Type.NUMBER_TYPE) != Type.DISJOINT) {
217                         exp = new NumericPromoter(exp, rt);
218                         try {
219                             exp = exp.simplify(env).typeCheck(env, AnyItemType.getInstance());
220                         } catch (XPathException err) {
221                             throw err.makeStatic();
222                         }
223                         suppliedItemType = (rt == StandardNames.XS_DOUBLE ? Type.DOUBLE_TYPE : Type.FLOAT_TYPE);
224                         suppliedCard = -1;
225                     }
226                 }
227
228                 // Rule 3b: promotion from anyURI -> string
229

230                 if (rt == Type.STRING &&
231                         Type.isSubType(suppliedItemType, Type.ANY_URI_TYPE)) {
232                     suppliedItemType = Type.STRING_TYPE;
233                     itemTypeOK = true;
234                         // we don't generate code to do a run-time type conversion; rather, we rely on
235
// operators and functions that accept a string to also accept an xs:anyURI. This
236
// is straightforward, because anyURIValue is a subclass of StringValue
237
}
238
239             }
240         }
241
242         // If both the cardinality and item type are statically OK, return now.
243
if (itemTypeOK && cardOK) {
244             return exp;
245         }
246
247         // If we haven't evaluated the cardinality of the supplied expression, do it now
248
if (suppliedCard == -1) {
249             suppliedCard = exp.getCardinality();
250             if (!cardOK) {
251                 cardOK = Cardinality.subsumes(reqCard, suppliedCard);
252             }
253         }
254
255         // If an empty sequence was explicitly supplied, and empty sequence is allowed,
256
// then the item type doesn't matter
257
if (cardOK && suppliedCard==StaticProperty.EMPTY) {
258             return exp;
259         }
260
261         // If the supplied value is () and () isn't allowed, fail now
262
if (suppliedCard==StaticProperty.EMPTY && ((reqCard & StaticProperty.ALLOWS_ZERO) == 0) ) {
263             StaticError err = new StaticError(
264                         "An empty sequence is not allowed as the " + role.getMessage(),
265                         getLocator(supplied, role));
266             err.setErrorCode(role.getErrorCode());
267             err.setIsTypeError(true);
268             throw err;
269         }
270
271         // Try a static type check. We only throw it out if the call cannot possibly succeed.
272

273         int relation = Type.relationship(suppliedItemType, reqItemType);
274         if (relation == Type.DISJOINT) {
275             // The item types may be disjoint, but if both the supplied and required types permit
276
// an empty sequence, we can't raise a static error. Raise a warning instead.
277
if (Cardinality.allowsZero(suppliedCard) &&
278                     Cardinality.allowsZero(reqCard)) {
279                 if (suppliedCard != StaticProperty.EMPTY) {
280                     String JavaDoc msg = "Required item type of " + role.getMessage() +
281                             " is " + reqItemType.toString(env.getNamePool()) +
282                             "; supplied value has item type " +
283                             suppliedItemType.toString(env.getNamePool()) +
284                             ". The expression can succeed only if the supplied value is an empty sequence.";
285                     env.issueWarning(msg, getLocator(supplied, role));
286                 }
287             } else {
288                 StaticError err = new StaticError(
289                     "Required item type of " + role.getMessage() +
290                         " is " + reqItemType.toString(env.getNamePool()) +
291                         "; supplied value has item type " +
292                         suppliedItemType.toString(env.getNamePool()),
293                     getLocator(supplied, role));
294                 err.setErrorCode(role.getErrorCode());
295                 err.setIsTypeError(true);
296                 throw err;
297             }
298         }
299
300         // Unless the type is guaranteed to match, add a dynamic type check,
301
// unless the value is already known in which case we might as well report
302
// the error now.
303

304         if (!(relation == Type.SAME_TYPE || relation == Type.SUBSUMED_BY)) {
305             if (exp instanceof Value) {
306                 StaticError err = new StaticError(
307                     "Required item type of " + role.getMessage() +
308                         " is " + reqItemType.toString(env.getNamePool()) +
309                         "; supplied value has item type " +
310                         suppliedItemType.toString(env.getNamePool()),
311                     getLocator(supplied, role));
312                 err.setErrorCode(role.getErrorCode());
313                 err.setIsTypeError(true);
314                 throw err;
315             }
316             ComputedExpression cexp = new ItemChecker(exp, reqItemType, role);
317             cexp.adoptChildExpression(exp);
318             exp = cexp;
319         }
320
321         if (!cardOK) {
322             if (exp instanceof Value) {
323                 StaticError err = new StaticError (
324                     "Required cardinality of " + role.getMessage() +
325                         " is " + Cardinality.toString(reqCard) +
326                         "; supplied value has cardinality " +
327                         Cardinality.toString(suppliedCard),
328                     getLocator(supplied, role));
329                 err.setIsTypeError(true);
330                 err.setErrorCode(role.getErrorCode());
331                 throw err;
332             } else {
333                 ComputedExpression cexp = CardinalityChecker.makeCardinalityChecker(exp, reqCard, role);
334                 cexp.adoptChildExpression(exp);
335                 exp = cexp;
336             }
337         }
338
339         return exp;
340     }
341
342      /**
343      * Check an expression against a required type, modifying it if necessary. This
344      * is a variant of the method {@link #staticTypeCheck} used for expressions that
345      * declare variables in XQuery. In these contexts, conversions such as numeric
346      * type promotion and atomization are not allowed.
347      *
348      * @param supplied The expression to be type-checked
349      * @param req The required type for the context in which the expression is used
350      * @param role Information about the role of the subexpression within the
351      * containing expression, used to provide useful error messages
352      * @param env The static context containing the types being checked. At present
353      * this is used only to locate a NamePool
354      * @return The original expression if it is type-safe, or the expression
355      * wrapped in a run-time type checking expression if not.
356      * @throws net.sf.saxon.trans.StaticError if the supplied type is statically inconsistent with the
357      * required type (that is, if they have no common subtype)
358      */

359
360     public static Expression strictTypeCheck(Expression supplied,
361                                              SequenceType req,
362                                              RoleLocator role,
363                                              StaticContext env)
364     throws StaticError {
365
366         // System.err.println("Strict Type Check on expression (requiredType = " + req + "):"); supplied.display(10);
367

368         Expression exp = supplied;
369
370         ItemType reqItemType = req.getPrimaryType();
371         int reqCard = req.getCardinality();
372
373         ItemType suppliedItemType = null;
374             // item type of the supplied expression: null means not yet calculated
375
int suppliedCard = -1;
376             // cardinality of the supplied expression: -1 means not yet calculated
377

378         boolean cardOK = (reqCard == StaticProperty.ALLOWS_ZERO_OR_MORE);
379         // Unless the required cardinality is zero-or-more (no constraints).
380
// check the static cardinality of the supplied expression
381
if (!cardOK) {
382             suppliedCard = exp.getCardinality();
383             cardOK = Cardinality.subsumes(reqCard, suppliedCard);
384         }
385
386         boolean itemTypeOK = req.getPrimaryType() instanceof AnyItemType;
387         // Unless the required item type and content type are ITEM (no constraints)
388
// check the static item type against the supplied expression.
389
// NOTE: we don't currently do any static inference regarding the content type
390
if (!itemTypeOK) {
391             suppliedItemType = exp.getItemType();
392             int relation = Type.relationship(reqItemType, suppliedItemType);
393             itemTypeOK = relation == Type.SAME_TYPE || relation == Type.SUBSUMES;
394         }
395
396         // If both the cardinality and item type are statically OK, return now.
397
if (itemTypeOK && cardOK) {
398             return exp;
399         }
400
401         // If we haven't evaluated the cardinality of the supplied expression, do it now
402
if (suppliedCard == -1) {
403             if (suppliedItemType instanceof NoNodeTest) {
404                 suppliedCard = StaticProperty.EMPTY;
405             } else {
406                 suppliedCard = exp.getCardinality();
407             }
408             if (!cardOK) {
409                 cardOK = Cardinality.subsumes(reqCard, suppliedCard);
410             }
411         }
412
413         // If an empty sequence was explicitly supplied, and empty sequence is allowed,
414
// then the item type doesn't matter
415
if (cardOK && suppliedCard==StaticProperty.EMPTY) {
416             return exp;
417         }
418
419         // If we haven't evaluated the item type of the supplied expression, do it now
420
if (suppliedItemType == null) {
421             suppliedItemType = exp.getItemType();
422         }
423
424         if (suppliedCard==StaticProperty.EMPTY && ((reqCard & StaticProperty.ALLOWS_ZERO) == 0) ) {
425             StaticError err = new StaticError(
426                         "An empty sequence is not allowed as the " + role.getMessage(),
427                         getLocator(supplied, role));
428             err.setErrorCode(role.getErrorCode());
429             err.setIsTypeError(true);
430             throw err;
431         }
432
433         // Try a static type check. We only throw it out if the call cannot possibly succeed.
434

435         int relation = Type.relationship(suppliedItemType, reqItemType);
436         if (relation == Type.DISJOINT) {
437             // The item types may be disjoint, but if both the supplied and required types permit
438
// an empty sequence, we can't raise a static error. Raise a warning instead.
439
if (Cardinality.allowsZero(suppliedCard) &&
440                     Cardinality.allowsZero(reqCard)) {
441                 if (suppliedCard != StaticProperty.EMPTY) {
442                     String JavaDoc msg = "Required item type of " + role.getMessage() +
443                             " is " + reqItemType.toString(env.getNamePool()) +
444                             "; supplied value has item type " +
445                             suppliedItemType.toString(env.getNamePool()) +
446                             ". The expression can succeed only if the supplied value is an empty sequence.";
447                     env.issueWarning(msg, getLocator(supplied, role));
448                 }
449             } else {
450                 StaticError err = new StaticError(
451                     "Required item type of " + role.getMessage() +
452                         " is " + reqItemType.toString(env.getNamePool()) +
453                         "; supplied value has item type " +
454                         suppliedItemType.toString(env.getNamePool()),
455                     getLocator(supplied, role));
456                 err.setErrorCode(role.getErrorCode());
457                 err.setIsTypeError(true);
458                 throw err;
459             }
460         }
461
462         // Unless the type is guaranteed to match, add a dynamic type check,
463
// unless the value is already known in which case we might as well report
464
// the error now.
465

466         if (!(relation == Type.SAME_TYPE || relation == Type.SUBSUMED_BY)) {
467                 ComputedExpression cexp = new ItemChecker(exp, reqItemType, role);
468                 cexp.adoptChildExpression(exp);
469                 exp = cexp;
470         }
471
472         if (!cardOK) {
473             if (exp instanceof Value) {
474                 StaticError err = new StaticError (
475                     "Required cardinality of " + role.getMessage() +
476                         " is " + Cardinality.toString(reqCard) +
477                         "; supplied value has cardinality " +
478                         Cardinality.toString(suppliedCard),
479                     getLocator(supplied, role));
480                 err.setIsTypeError(true);
481                 err.setErrorCode(role.getErrorCode());
482                 throw err;
483             } else {
484                 ComputedExpression cexp = CardinalityChecker.makeCardinalityChecker(exp, reqCard, role);
485                 cexp.adoptChildExpression(exp);
486                 exp = cexp;
487             }
488         }
489
490         return exp;
491     }
492
493     /**
494      * Get a SourceLocator given an expression and a role
495      */

496
497     private static SourceLocator JavaDoc getLocator(Expression supplied, RoleLocator role) {
498         SourceLocator JavaDoc loc = ExpressionTool.getLocator(supplied);
499         if (loc == null) {
500             loc = role.getSourceLocator();
501         }
502         return loc;
503     }
504
505     /**
506      * Test whether a given value conforms to a given type
507      * @param val the value
508      * @param requiredType the required type
509      * @return a DynamicError describing the error condition if the value doesn't conform;
510      * or null if it does.
511      */

512
513     public static DynamicError testConformance(Value val, SequenceType requiredType) {
514         ItemType reqItemType = requiredType.getPrimaryType();
515         if (!Type.isSubType(val.getItemType(), reqItemType)) {
516             DynamicError err = new DynamicError (
517                     "Global parameter requires type " + reqItemType +
518                     "; supplied value has type " + val.getItemType());
519             err.setIsTypeError(true);
520             return err;
521         }
522         int reqCardinality = requiredType.getCardinality();
523         if (!Cardinality.subsumes(reqCardinality, val.getCardinality())) {
524             DynamicError err = new DynamicError (
525                     "Supplied value of external parameter does not match the required cardinality");
526             err.setIsTypeError(true);
527             return err;
528         }
529         return null;
530     }
531 }
532
533 //
534
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
535
// you may not use this file except in compliance with the License. You may obtain a copy of the
536
// License at http://www.mozilla.org/MPL/
537
//
538
// Software distributed under the License is distributed on an "AS IS" basis,
539
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
540
// See the License for the specific language governing rights and limitations under the License.
541
//
542
// The Original Code is: all this file.
543
//
544
// The Initial Developer of the Original Code is Michael H. Kay
545
//
546
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
547
//
548
// Contributor(s): none.
549
//
550
Popular Tags