KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.om.Item;
4 import net.sf.saxon.trans.DynamicError;
5 import net.sf.saxon.trans.StaticError;
6 import net.sf.saxon.trans.XPathException;
7 import net.sf.saxon.type.ItemType;
8 import net.sf.saxon.type.Type;
9 import net.sf.saxon.type.AtomicType;
10 import net.sf.saxon.value.*;
11 import net.sf.saxon.ConversionContext;
12 import net.sf.saxon.Err;
13
14 /**
15  * Arithmetic Expression: an expression using one of the operators
16  * plus, minus, multiply, div, idiv, mod.
17  */

18
19 class ArithmeticExpression extends BinaryExpression {
20
21     private static final class Signature {
22         ItemType operand0;
23         ItemType operand1;
24         int operation;
25         ItemType resultType;
26
27         Signature(ItemType t0, ItemType t1, int op, ItemType r) {
28             operand0 = t0;
29             operand1 = t1;
30             operation = op;
31             resultType = r;
32         }
33     }
34
35     private static final int NUMERIC_ARITHMETIC = 0;
36     private static final int DATE_AND_DURATION = 1;
37     private static final int DATE_DIFFERENCE = 2;
38     private static final int DURATION_ADDITION = 3;
39     private static final int DURATION_MULTIPLICATION = 4;
40     private static final int DURATION_DIVISION = 5;
41
42     private static final int UNKNOWN = -1;
43     private static final int UNKNOWN_10 = -2;
44
45     private static final Signature[] plusTable = {
46         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
47         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
48         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
49         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
50
51         new Signature(Type.DATE_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.DATE_TYPE),
52         new Signature(Type.DURATION_TYPE, Type.DATE_TYPE, DATE_AND_DURATION, Type.DATE_TYPE),
53
54         new Signature(Type.TIME_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.TIME_TYPE),
55         new Signature(Type.DURATION_TYPE, Type.TIME_TYPE, DATE_AND_DURATION, Type.TIME_TYPE),
56
57         new Signature(Type.DATE_TIME_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.DATE_TIME_TYPE),
58         new Signature(Type.DURATION_TYPE, Type.DATE_TIME_TYPE, DATE_AND_DURATION, Type.DATE_TIME_TYPE),
59
60         new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE, DURATION_ADDITION, Type.DURATION_TYPE)
61
62     };
63
64     private static final Signature[] minusTable = {
65         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
66         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
67         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
68         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
69
70         new Signature(Type.DATE_TYPE, Type.DATE_TYPE, DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
71         new Signature(Type.DATE_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.DATE_TYPE),
72
73         new Signature(Type.TIME_TYPE, Type.TIME_TYPE, DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
74         new Signature(Type.TIME_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.TIME_TYPE),
75
76         new Signature(Type.DATE_TIME_TYPE, Type.DATE_TIME_TYPE, DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
77         new Signature(Type.DATE_TIME_TYPE, Type.DURATION_TYPE, DATE_AND_DURATION, Type.DATE_TIME_TYPE),
78
79         new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE, DURATION_ADDITION, Type.DURATION_TYPE)
80
81     };
82
83     private static final Signature[] multiplyTable = {
84         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
85         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
86         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
87         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
88
89         new Signature(Type.NUMBER_TYPE, Type.DURATION_TYPE, DURATION_MULTIPLICATION, Type.DURATION_TYPE),
90         new Signature(Type.DURATION_TYPE, Type.NUMBER_TYPE, DURATION_MULTIPLICATION, Type.DURATION_TYPE)
91     };
92
93     private static final Signature[] divideTable = {
94         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
95         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
96         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
97         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
98
99         new Signature(Type.DURATION_TYPE, Type.NUMBER_TYPE, DURATION_MULTIPLICATION, Type.DURATION_TYPE),
100         new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE, DURATION_DIVISION, Type.NUMBER_TYPE)
101     };
102
103     private static final Signature[] idivTable = {
104         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
105         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
106         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
107         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.INTEGER_TYPE)
108     };
109
110     private static final Signature[] modTable = {
111         new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
112         new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
113         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
114         new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC, Type.NUMBER_TYPE)
115     };
116
117     private boolean backwardsCompatible = false;
118
119     public ArithmeticExpression(Expression p1, int operator, Expression p2) {
120         super(p1, operator, p2);
121     }
122
123     /**
124      * Type-check the expression statically. We try to work out which particular
125      * arithmetic function to use if the types of operands are known an compile time.
126      */

127
128     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
129
130         backwardsCompatible = env.isInBackwardsCompatibleMode();
131
132         operand0 = operand0.typeCheck(env, contextItemType);
133         operand1 = operand1.typeCheck(env, contextItemType);
134
135
136         SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
137         // TODO: this is using the function call rules. Arithetic expressions have slightly different rules.
138

139         RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0, null);
140         role0.setSourceLocator(this);
141         operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, backwardsCompatible, role0, env);
142         // System.err.println("First operand"); operand0.display(10);
143

144         RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1, null);
145         role1.setSourceLocator(this);
146         operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, backwardsCompatible, role1, env);
147         // System.err.println("Second operand"); operand1.display(10);
148

149         if (backwardsCompatible) {
150             Expression exp = new Arithmetic10(operand0, operator, operand1);
151             return exp.simplify(env).typeCheck(env, contextItemType);
152         }
153
154         Expression e = super.typeCheck(env, contextItemType);
155         if (e instanceof ArithmeticExpression) {
156             AtomicType type0 = (AtomicType)operand0.getItemType().getPrimitiveItemType();
157             AtomicType type1 = (AtomicType)operand1.getItemType().getPrimitiveItemType();
158             if (backwardsCompatible) {
159                 if (type0 == Type.BOOLEAN_TYPE) {
160                     type0 = Type.NUMBER_TYPE;
161                 } else if (type0 == Type.STRING_TYPE) {
162                     type0 = Type.NUMBER_TYPE;
163                 }
164                 if (type1 == Type.BOOLEAN_TYPE) {
165                     type1 = Type.NUMBER_TYPE;
166                 } else if (type1 == Type.STRING_TYPE) {
167                     type1 = Type.NUMBER_TYPE;
168                 }
169             }
170             final int action = getAction(type0, operator, type1, backwardsCompatible);
171             switch (action) {
172                 case NUMERIC_ARITHMETIC:
173                     e = new NumericArithmetic(operand0, operator, operand1);
174                     break;
175                 case DURATION_ADDITION:
176                     e = new DurationAddition(operand0, operator, operand1);
177                     break;
178                 case DURATION_MULTIPLICATION:
179                     e = new DurationMultiplication(operand0, operator, operand1);
180                     break;
181                 case DURATION_DIVISION:
182                     e = new DurationDivision(operand0, operator, operand1);
183                     break;
184                 case DATE_AND_DURATION:
185                     e = new DateAndDuration(operand0, operator, operand1);
186                     break;
187                 case DATE_DIFFERENCE:
188                     e = new DateDifference(operand0, operator, operand1);
189                     break;
190
191                 case UNKNOWN_10:
192                     e = new Arithmetic10(operand0, operator, operand1);
193                     break;
194
195                 case UNKNOWN:
196                     // either the types are not known yet, or they are wrong
197
if (Type.isSubType(type0, Type.ANY_ATOMIC_TYPE) &&
198                             type0 != Type.UNTYPED_ATOMIC_TYPE &&
199                             type0 != Type.ANY_ATOMIC_TYPE &&
200                             Type.isSubType(type1, Type.ANY_ATOMIC_TYPE) &&
201                             type1 != Type.UNTYPED_ATOMIC_TYPE &&
202                             type1 != Type.ANY_ATOMIC_TYPE) {
203                         StaticError err = new StaticError("Unsuitable operands for arithmetic operation (" +
204                                 type0.toString(env.getNamePool()) + ", " +
205                                 type1.toString(env.getNamePool()) + ')');
206                         err.setErrorCode("XPTY0004");
207                         err.setIsTypeError(true);
208                         err.setLocator(ExpressionTool.getLocator(this));
209                         throw err;
210                     }
211                     return e;
212             }
213             ExpressionTool.copyLocationInfo(this, e);
214             if (e instanceof ComputedExpression) {
215                 ((ComputedExpression)e).setParentExpression(getParentExpression());
216             }
217             try {
218                 if (operand0 instanceof Value && operand1 instanceof Value) {
219                     return ExpressionTool.eagerEvaluate(e, null);
220                 }
221             } catch (DynamicError err) {
222                 // Defer any error reporting until run-time
223
}
224         }
225         return e;
226     }
227
228
229     /**
230      * Decide what action is required based on types of operands
231      */

232
233     private static int getAction(AtomicType type1, int operator, AtomicType type2, boolean backwardsCompatible) {
234         if (type1.getFingerprint() == Type.DAY_TIME_DURATION) {
235             type1 = Type.DURATION_TYPE;
236         } else if (type1.getFingerprint() == Type.YEAR_MONTH_DURATION) {
237             type1 = Type.DURATION_TYPE;
238         }
239         if (type2.getFingerprint() == Type.DAY_TIME_DURATION) {
240             type2 = Type.DURATION_TYPE;
241         } else if (type2.getFingerprint() == Type.YEAR_MONTH_DURATION) {
242             type2 = Type.DURATION_TYPE;
243         }
244         Signature[] table = getOperatorTable(operator);
245         int entry = getEntry(table, type1, type2);
246         if (entry < 0) {
247             return (backwardsCompatible ? UNKNOWN_10 : UNKNOWN);
248         }
249         return table[entry].operation;
250     }
251
252     private static Signature[] getOperatorTable(int operator) {
253         switch (operator) {
254             case Token.PLUS:
255                 return plusTable;
256             case Token.MINUS:
257             case Token.NEGATE:
258                 return minusTable;
259             case Token.MULT:
260                 return multiplyTable;
261             case Token.DIV:
262                 return divideTable;
263             case Token.IDIV:
264                 return idivTable;
265             case Token.MOD:
266                 return modTable;
267             default:
268                 throw new IllegalArgumentException JavaDoc("Unknown arithmetic operator");
269         }
270     }
271
272     private static int getEntry(Signature[] table, ItemType type1, ItemType type2) {
273         if (Type.isNumericPrimitiveType(type1)) {
274             type1 = Type.NUMBER_TYPE;
275         }
276         if (Type.isNumericPrimitiveType(type2)) {
277             type2 = Type.NUMBER_TYPE;
278         }
279         for (int i = 0; i < table.length; i++) {
280             if (type1.equals(table[i].operand0) &&
281                     type2.equals(table[i].operand1)) {
282                 return i;
283             }
284         }
285         return -1;
286     }
287
288     /**
289      * Determine the data type of the expression, if this is known statically
290      */

291
292     public ItemType getItemType() {
293         final ItemType t1 = operand0.getItemType();
294         final ItemType pt1 = t1.getPrimitiveItemType();
295         final ItemType t2 = operand1.getItemType();
296         final ItemType pt2 = t2.getPrimitiveItemType();
297         final Signature[] table = getOperatorTable(operator);
298         final int entry = getEntry(table, pt1, pt2);
299
300         if (entry < 0) {
301             return Type.ANY_ATOMIC_TYPE; // type is not known statically
302
}
303
304         ItemType resultType = table[entry].resultType;
305         if (resultType == Type.NUMBER_TYPE) {
306             resultType = NumericValue.promote(pt1, pt2);
307             // exception: integer div integer => decimal
308
if (operator == Token.DIV && resultType == Type.INTEGER_TYPE) {
309                 resultType = Type.DECIMAL_TYPE;
310             }
311         } else if (resultType == Type.DURATION_TYPE) {
312             // if one of the operands is a subtype of duration, then the result will be the same subtype
313
// (this isn't captured in the table because these types are not primitive).
314
if (Type.isSubType(t1, Type.DAY_TIME_DURATION_TYPE)) {
315                 resultType = Type.DAY_TIME_DURATION_TYPE;
316             } else if (Type.isSubType(t2, Type.DAY_TIME_DURATION_TYPE)) {
317                 resultType = Type.DAY_TIME_DURATION_TYPE;
318             } else if (Type.isSubType(t1, Type.YEAR_MONTH_DURATION_TYPE)) {
319                 resultType = Type.YEAR_MONTH_DURATION_TYPE;
320             } else if (Type.isSubType(t2, Type.YEAR_MONTH_DURATION_TYPE)) {
321                 resultType = Type.YEAR_MONTH_DURATION_TYPE;
322             }
323         }
324         return resultType;
325     }
326
327     /**
328      * Evaluate the expression. We only take this path if the type could not be determined
329      * statically.
330      */

331
332     public Item evaluateItem(XPathContext context) throws XPathException {
333
334         AtomicValue v1 = (AtomicValue)operand0.evaluateItem(context);
335         if (v1 == null) {
336             return null;
337         }
338         v1 = v1.getPrimitiveValue();
339
340         AtomicValue v2 = (AtomicValue)operand1.evaluateItem(context);
341         if (v2 == null) {
342             return null;
343         }
344         v2 = v2.getPrimitiveValue();
345
346         int action = getAction((AtomicType)v1.getItemType().getPrimitiveItemType(),
347                 operator,
348                 (AtomicType)v2.getItemType().getPrimitiveItemType(),
349                 backwardsCompatible);
350
351         Expression e;
352         switch (action) {
353             case NUMERIC_ARITHMETIC:
354                 e = new NumericArithmetic(v1, operator, v2);
355                 break;
356             case DURATION_ADDITION:
357                 e = new DurationAddition(v1, operator, v2);
358                 break;
359             case DURATION_MULTIPLICATION:
360                 e = new DurationMultiplication(v1, operator, v2);
361                 break;
362             case DURATION_DIVISION:
363                 e = new DurationDivision(v1, operator, v2);
364                 break;
365             case DATE_AND_DURATION:
366                 e = new DateAndDuration(v1, operator, v2);
367                 break;
368             case DATE_DIFFERENCE:
369                 e = new DateDifference(v1, operator, v2);
370                 break;
371             default:
372                 // types are not known yet. Force to double if in 1.0 mode
373
if (backwardsCompatible) {
374                     NumericValue nv1;
375                     NumericValue nv2;
376                     try {
377                         nv1 = (NumericValue)v1.convert(Type.DOUBLE, context);
378                         nv2 = (NumericValue)v2.convert(Type.DOUBLE, context);
379                     } catch (XPathException err) {
380                         typeError("Unsuitable operands for arithmetic operation (" +
381                                 v1.getItemType() + ", " +
382                                 v2.getItemType() + ')', "XPTY0004", context);
383                         return null;
384                     }
385                     e = new NumericArithmetic(nv1, operator, nv2);
386                 } else {
387                     typeError("Unsuitable operands for arithmetic operation (" +
388                             v1.getItemType() + ", " +
389                             v2.getItemType() + ')', "XPTY0004", context);
390                     return null;
391                 }
392         }
393         ExpressionTool.copyLocationInfo(this, e);
394         if (e instanceof ComputedExpression) {
395             ((ComputedExpression)e).setParentExpression(getParentExpression());
396         }
397         return e.evaluateItem(context);
398     }
399
400     /**
401      * Inner class to handle 1.0 backwards compatible arithmetic
402      */

403     private static class Arithmetic10 extends BinaryExpression {
404
405         public boolean backwardsCompatible;
406
407         public Arithmetic10(Expression p1, int operator, Expression p2) {
408             super(p1, operator, p2);
409         }
410
411         public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
412
413             SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
414
415             RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0, null);
416             role0.setSourceLocator(this);
417             operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, backwardsCompatible, role0, env);
418
419             RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1, null);
420             role1.setSourceLocator(this);
421             operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, backwardsCompatible, role1, env);
422
423             Expression e = super.typeCheck(env, contextItemType);
424             if (e instanceof ArithmeticExpression) {
425                 AtomicType type0 = (AtomicType)operand0.getItemType().getPrimitiveItemType();
426                 AtomicType type1 = (AtomicType)operand1.getItemType().getPrimitiveItemType();
427                 if (backwardsCompatible) {
428                     if (type0 == Type.BOOLEAN_TYPE) {
429                         type0 = Type.NUMBER_TYPE;
430                     } else if (type0 == Type.STRING_TYPE) {
431                         type0 = Type.NUMBER_TYPE;
432                     }
433                     if (type1 == Type.BOOLEAN_TYPE) {
434                         type1 = Type.NUMBER_TYPE;
435                     } else if (type1 == Type.STRING_TYPE) {
436                         type1 = Type.NUMBER_TYPE;
437                     }
438                 }
439                 final int action = getAction(type0, operator, type1, backwardsCompatible);
440                 switch (action) {
441                     case NUMERIC_ARITHMETIC:
442                         e = new NumericArithmetic(operand0, operator, operand1);
443                         ((NumericArithmetic)e).setBackwardsCompatible(backwardsCompatible);
444                         break;
445                     case DURATION_ADDITION:
446                         e = new DurationAddition(operand0, operator, operand1);
447                         break;
448                     case DURATION_MULTIPLICATION:
449                         e = new DurationMultiplication(operand0, operator, operand1);
450                         break;
451                     case DURATION_DIVISION:
452                         e = new DurationDivision(operand0, operator, operand1);
453                         break;
454                     case DATE_AND_DURATION:
455                         e = new DateAndDuration(operand0, operator, operand1);
456                         break;
457                     case DATE_DIFFERENCE:
458                         e = new DateDifference(operand0, operator, operand1);
459                         break;
460
461                     case UNKNOWN_10:
462                         e = new Arithmetic10(operand0, operator, operand1);
463                         break;
464
465                     case UNKNOWN:
466                         // either the types are not known yet, or they are wrong
467
if (Type.isSubType(type0, Type.ANY_ATOMIC_TYPE) &&
468                                 type0 != Type.UNTYPED_ATOMIC_TYPE &&
469                                 type0 != Type.ANY_ATOMIC_TYPE &&
470                                 Type.isSubType(type1, Type.ANY_ATOMIC_TYPE) &&
471                                 type1 != Type.UNTYPED_ATOMIC_TYPE &&
472                                 type1 != Type.ANY_ATOMIC_TYPE) {
473                             StaticError err = new StaticError("Unsuitable operands for arithmetic operation (" +
474                                     type0.toString(env.getNamePool()) + ", " +
475                                     type1.toString(env.getNamePool()) + ')');
476                             err.setErrorCode("XPTY0004");
477                             err.setIsTypeError(true);
478                             throw err;
479                         }
480                         return e;
481                 }
482                 ExpressionTool.copyLocationInfo(this, e);
483                 if (e instanceof ComputedExpression) {
484                     ((ComputedExpression)e).setParentExpression(getParentExpression());
485                 }
486                 try {
487                     if (operand0 instanceof Value && operand1 instanceof Value) {
488                         return ExpressionTool.eagerEvaluate(e, null);
489                     }
490                 } catch (DynamicError err) {
491                     // Defer any error reporting until run-time
492
}
493             }
494             return e;
495         }
496
497
498         /**
499          * Determine the data type of the expression, if possible. All expression return
500          * sequences, in general; this method determines the type of the items within the
501          * sequence, assuming that (a) this is known in advance, and (b) it is the same for
502          * all items in the sequence.
503          * <p/>
504          * <p>This method should always return a result, though it may be the best approximation
505          * that is available at the time.</p>
506          *
507          * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
508          * Type.NODE, or Type.ITEM (meaning not known at compile time)
509          */

510
511         public ItemType getItemType() {
512             return Type.ANY_ATOMIC_TYPE;
513         }
514
515         /**
516          * Evaluate the expression.
517          */

518
519         public Item evaluateItem(XPathContext context) throws XPathException {
520
521             AtomicValue v1 = (AtomicValue)operand0.evaluateItem(context);
522             if (v1 == null) {
523                 return DoubleValue.NaN;
524             }
525             v1 = v1.getPrimitiveValue();
526             if (v1 instanceof BooleanValue || v1 instanceof StringValue || v1 instanceof NumericValue) {
527                 try {
528                     v1 = v1.convert(Type.DOUBLE, context);
529                 } catch (XPathException e) {
530                     return DoubleValue.NaN;
531                 }
532             }
533
534             AtomicValue v2 = (AtomicValue)operand1.evaluateItem(context);
535             if (v2 == null) {
536                 return DoubleValue.NaN;
537             }
538             v2 = v2.getPrimitiveValue();
539             if (v2 instanceof BooleanValue || v2 instanceof StringValue || v2 instanceof NumericValue) {
540                 try {
541                     v2 = v2.convert(Type.DOUBLE, context);
542                 } catch (XPathException e) {
543                     return DoubleValue.NaN;
544                 }
545             }
546
547             int action = getAction((AtomicType)v1.getItemType().getPrimitiveItemType(),
548                     operator,
549                     (AtomicType)v2.getItemType().getPrimitiveItemType(),
550                     backwardsCompatible);
551
552             Expression e;
553             switch (action) {
554                 case NUMERIC_ARITHMETIC:
555                     try {
556                         return NumericArithmetic.doArithmetic(v1, operator, v2, context, backwardsCompatible);
557                     } catch (XPathException ex) {
558                         if (ex.getLocator() == null) {
559                             ex.setLocator(this);
560                         }
561                         throw ex;
562                     }
563
564                 case DURATION_ADDITION:
565                     try {
566                         return DurationAddition.doArithmetic(v1, operator, v2, context);
567                     } catch (XPathException ex) {
568                         if (ex.getLocator() == null) {
569                             ex.setLocator(this);
570                         }
571                         throw ex;
572                     }
573
574                 case DURATION_MULTIPLICATION:
575                     try {
576                         return DurationMultiplication.doArithmetic(v1, operator, v2, context);
577                     } catch (XPathException ex) {
578                         if (ex.getLocator() == null) {
579                             ex.setLocator(this);
580                         }
581                         throw ex;
582                     }
583
584                 case DURATION_DIVISION:
585                     try {
586                         return DurationDivision.doArithmetic(v1, v2, context);
587                     } catch (XPathException ex) {
588                         if (ex.getLocator() == null) {
589                             ex.setLocator(this);
590                         }
591                         throw ex;
592                     }
593
594                 case DATE_AND_DURATION:
595                     try {
596                         return DateAndDuration.doArithmetic(v1, operator, v2, context);
597                     } catch (XPathException ex) {
598                         if (ex.getLocator() == null) {
599                             ex.setLocator(this);
600                         }
601                         throw ex;
602                     }
603
604                 case DATE_DIFFERENCE:
605                     try {
606                         ConversionContext cc = context;
607                         if (cc == null) {
608                             cc = getExecutable().getConfiguration();
609                         }
610                         return DateDifference.doArithmetic(v1, v2, context, cc);
611                     } catch (XPathException ex) {
612                         if (ex.getLocator() == null) {
613                             ex.setLocator(this);
614                         }
615                         throw ex;
616                     }
617
618                 default:
619                     typeError("Unsuitable operands for arithmetic operation (" +
620                             v1.getItemType() + ", " +
621                             v2.getItemType() + ')', "XPTY0004", context);
622                     return null;
623             }
624
625         }
626     }
627
628     /**
629      * Inner class to handle numeric arithmetic expressions
630      */

631
632     public static class NumericArithmetic extends ArithmeticExpression {
633
634         boolean backwardsCompatible = false;
635
636         public NumericArithmetic(Expression p1, int operator, Expression p2) {
637             super(p1, operator, p2);
638         }
639
640         public void setBackwardsCompatible(boolean flag) {
641             backwardsCompatible = flag;
642         }
643
644         /**
645          * Evaluate the expression.
646          */

647
648         public Item evaluateItem(XPathContext context) throws XPathException {
649             try {
650                 return doArithmetic(operand0, operator, operand1, context, backwardsCompatible);
651             } catch (XPathException err) {
652                 if (err.getLocator() == null) {
653                     err.setLocator(this);
654                 }
655                 throw err;
656             }
657         }
658
659         public static Item doArithmetic(Expression operand0, int operator, Expression operand1,
660                                         XPathContext context, boolean backwardsCompatible)
661                 throws XPathException {
662
663
664             AtomicValue v1 = ((AtomicValue)operand0.evaluateItem(context));
665             if (v1 == null) {
666                 return null;
667             }
668
669             if (v1 instanceof UntypedAtomicValue) {
670                 try {
671                     v1 = new DoubleValue(Value.stringToNumber(v1.getStringValueCS()));
672                 } catch (NumberFormatException JavaDoc e) {
673                    if (backwardsCompatible) {
674                         v1 = DoubleValue.NaN;
675                     } else {
676                         DynamicError err = new DynamicError("Failure converting untyped value " +
677                             Err.wrap(v1.getStringValueCS(), Err.VALUE) + " to a number");
678                         err.setErrorCode("FORG0001");
679                         err.setXPathContext(context);
680                         throw err;
681                     }
682                 }
683             } else {
684                 v1 = v1.getPrimitiveValue();
685             }
686             AtomicValue v2 = ((AtomicValue)operand1.evaluateItem(context));
687             if (v2 == null) {
688                 return null;
689             }
690
691             if (v2 instanceof UntypedAtomicValue) {
692                 try {
693                     v2 = new DoubleValue(Value.stringToNumber(v2.getStringValueCS()));
694                 } catch (NumberFormatException JavaDoc e) {
695                     if (backwardsCompatible) {
696                         v2 = DoubleValue.NaN;
697                     } else {
698                         DynamicError err = new DynamicError("Failure converting untyped value " +
699                             Err.wrap(v2.getStringValueCS(), Err.VALUE) + " to a number");
700                         err.setErrorCode("FORG0001");
701                         err.setXPathContext(context);
702                         throw err;
703                     }
704                 }
705             } else {
706                 v2 = v2.getPrimitiveValue();
707             }
708
709             if (operator == Token.NEGATE) {
710                 return ((NumericValue)v2).negate();
711             } else {
712                 try {
713                     return ((NumericValue)v1).arithmetic(operator, (NumericValue)v2, context);
714                 } catch (DynamicError err) {
715                     if (err.getXPathContext() == null) {
716                         err.setXPathContext(context);
717                     }
718                     throw err;
719                 } catch (ArithmeticException JavaDoc err) {
720                     DynamicError e = new DynamicError("Arithmetic exception: " + err.getMessage());
721                     e.setXPathContext(context);
722                     throw e;
723                 }
724             }
725         }
726     }
727
728     /**
729      * Inner class to handle addition and subtraction of two durations
730      */

731
732     public static class DurationAddition extends ArithmeticExpression {
733
734         public DurationAddition(Expression p1, int operator, Expression p2) {
735             super(p1, operator, p2);
736         }
737
738         /**
739          * Evaluate the expression.
740          */

741
742         public Item evaluateItem(XPathContext context) throws XPathException {
743             try {
744                 return doArithmetic(operand0, operator, operand1, context);
745             } catch (XPathException err) {
746                 if (err.getLocator() == null) {
747                     err.setLocator(this);
748                 }
749                 throw err;
750             }
751         }
752
753         public static Item doArithmetic(Expression operand0, int operator, Expression operand1, XPathContext context)
754         throws XPathException {
755
756             AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context);
757             if (av1 == null) {
758                 return null;
759             }
760             DurationValue v1 = (DurationValue)av1.getPrimitiveValue();
761
762             AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context);
763             if (av2 == null) {
764                 return null;
765             }
766             DurationValue v2 = (DurationValue)av2.getPrimitiveValue();
767
768             if (operator == Token.PLUS) {
769                 return v1.add(v2, context);
770             } else if (operator == Token.MINUS) {
771                 return v1.subtract(v2, context);
772             } else {
773                 throw new AssertionError JavaDoc("Unknown operation on durations");
774             }
775
776         }
777     }
778
779     /**
780      * Inner class to handle multiplication (or division) of a duration by a number
781      */

782
783     public static class DurationMultiplication extends ArithmeticExpression {
784
785         public DurationMultiplication(Expression p1, int operator, Expression p2) {
786
787             // by the time we get here, we know that one of the operands is a duration,
788
// but it might be either one. We make it the first.
789

790             super(p1, operator, p2);
791             if (Type.isSubType(p2.getItemType(), Type.DURATION_TYPE)) {
792                 operand0 = p2;
793                 operand1 = p1;
794             }
795         }
796
797         /**
798          * Evaluate the expression.
799          */

800
801         public Item evaluateItem(XPathContext context) throws XPathException {
802             try {
803                 return doArithmetic(operand0, operator, operand1, context);
804             } catch (XPathException err) {
805                 if (err.getLocator() == null) {
806                     err.setLocator(this);
807                 }
808                 throw err;
809             }
810         }
811
812         public static Item doArithmetic(Expression operand0, int operator, Expression operand1, XPathContext context)
813         throws XPathException {
814
815             AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context);
816             if (av1 == null) {
817                 return null;
818             }
819             DurationValue v1 = (DurationValue)av1.getPrimitiveValue();
820
821             AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context);
822             if (av2 == null) {
823                 return null;
824             }
825             NumericValue v2 = (NumericValue)av2.getPrimitiveValue();
826
827             double d = v2.getDoubleValue();
828
829             if (operator == Token.DIV) {
830                 d = 1.0 / d;
831             }
832
833             return v1.multiply(d, context);
834
835         }
836     }
837
838     /**
839      * Inner class to handle division of two durations to give a number
840      */

841
842     public static class DurationDivision extends ArithmeticExpression {
843
844         public DurationDivision(Expression p1, int operator, Expression p2) {
845             super(p1, operator, p2);
846         }
847
848         /**
849          * Evaluate the expression.
850          */

851
852         public Item evaluateItem(XPathContext context) throws XPathException {
853             try {
854                 return doArithmetic(operand0, operand1, context);
855             } catch (XPathException err) {
856                 if (err.getLocator() == null) {
857                     err.setLocator(this);
858                 }
859                 throw err;
860             }
861         }
862
863         public static Item doArithmetic(Expression operand0, Expression operand1, XPathContext context)
864         throws XPathException {
865
866             AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context);
867             if (av1 == null) {
868                 return null;
869             }
870             DurationValue v1 = (DurationValue)av1.getPrimitiveValue();
871
872             AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context);
873             if (av2 == null) {
874                 return null;
875             }
876             DurationValue v2 = (DurationValue)av2.getPrimitiveValue();
877
878             return v1.divide(v2, context);
879
880         }
881     }
882
883
884     /**
885      * Inner class to handle addition or subtraction of a Date (or Time, or DateTime) and a Duration
886      */

887
888     public static class DateAndDuration extends ArithmeticExpression {
889
890         public DateAndDuration(Expression p1, int operator, Expression p2) {
891
892             // by the time we get here, we know that one of the operands is a duration,
893
// but it might be either one. We make it the second.
894

895             super(p1, operator, p2);
896             if (Type.isSubType(p1.getItemType(), Type.DURATION_TYPE)) {
897                 operand0 = p2;
898                 operand1 = p1;
899             }
900
901         }
902
903         /**
904          * Evaluate the expression.
905          */

906
907         public Item evaluateItem(XPathContext context) throws XPathException {
908             try {
909                 return doArithmetic(operand0, operator, operand1, context);
910             } catch (XPathException err) {
911                 if (err.getLocator() == null) {
912                     err.setLocator(this);
913                 }
914                 throw err;
915             }
916         }
917
918         public static Item doArithmetic(Expression operand0, int operator, Expression operand1, XPathContext context)
919         throws XPathException {
920             AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context);
921             if (av1 == null) {
922                 return null;
923             }
924             CalendarValue v1 = (CalendarValue)av1.getPrimitiveValue();
925
926             AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context);
927             if (av2 == null) {
928                 return null;
929             }
930             DurationValue v2 = (DurationValue)av2.getPrimitiveValue();
931
932             if (operator == Token.MINUS) {
933                 v2 = v2.multiply(-1.0, context);
934             }
935
936             return v1.add(v2);
937
938         }
939     }
940
941     /**
942      * Inner class to handle subtraction of a Date (or Time, or DateTime) from another, to return a Duration
943      */

944
945     public static class DateDifference extends ArithmeticExpression {
946
947         public DateDifference(Expression p1, int operator, Expression p2) {
948             super(p1, operator, p2);
949         }
950
951         /**
952          * Evaluate the expression.
953          */

954
955         public Item evaluateItem(XPathContext context) throws XPathException {
956             try {
957                 ConversionContext cc = context;
958                 if (cc == null) {
959                     cc = getExecutable().getConfiguration();
960                 }
961                 return doArithmetic(operand0, operand1, context, cc);
962             } catch (XPathException err) {
963                 if (err.getLocator() == null) {
964                     err.setLocator(this);
965                 }
966                 throw err;
967             }
968         }
969
970         public static Item doArithmetic(Expression operand0, Expression operand1, XPathContext context, ConversionContext cc)
971         throws XPathException {
972             AtomicValue av1 = (AtomicValue)operand0.evaluateItem(context);
973             if (av1 == null) {
974                 return null;
975             }
976             CalendarValue v1 = (CalendarValue)av1.getPrimitiveValue();
977
978             AtomicValue av2 = (AtomicValue)operand1.evaluateItem(context);
979             if (av2 == null) {
980                 return null;
981             }
982             CalendarValue v2 = (CalendarValue)av2.getPrimitiveValue();
983
984             return v1.subtract(v2, cc);
985
986         }
987     }
988
989 }
990
991 //
992
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
993
// you may not use this file except in compliance with the License. You may obtain a copy of the
994
// License at http://www.mozilla.org/MPL/
995
//
996
// Software distributed under the License is distributed on an "AS IS" basis,
997
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
998
// See the License for the specific language governing rights and limitations under the License.
999
//
1000
// The Original Code is: all this file.
1001
//
1002
// The Initial Developer of the Original Code is Michael H. Kay
1003
//
1004
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1005
//
1006
// Contributor(s): none.
1007
//
1008
Popular Tags