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 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 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 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 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 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 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 } 224 } 225 return e; 226 } 227 228 229 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 ("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 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; } 303 304 ItemType resultType = table[entry].resultType; 305 if (resultType == Type.NUMBER_TYPE) { 306 resultType = NumericValue.promote(pt1, pt2); 307 if (operator == Token.DIV && resultType == Type.INTEGER_TYPE) { 309 resultType = Type.DECIMAL_TYPE; 310 } 311 } else if (resultType == Type.DURATION_TYPE) { 312 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 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 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 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 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 } 493 } 494 return e; 495 } 496 497 498 510 511 public ItemType getItemType() { 512 return Type.ANY_ATOMIC_TYPE; 513 } 514 515 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 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 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 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 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 err) { 720 DynamicError e = new DynamicError("Arithmetic exception: " + err.getMessage()); 721 e.setXPathContext(context); 722 throw e; 723 } 724 } 725 } 726 } 727 728 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 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 ("Unknown operation on durations"); 774 } 775 776 } 777 } 778 779 782 783 public static class DurationMultiplication extends ArithmeticExpression { 784 785 public DurationMultiplication(Expression p1, int operator, Expression p2) { 786 787 790 super(p1, operator, p2); 791 if (Type.isSubType(p2.getItemType(), Type.DURATION_TYPE)) { 792 operand0 = p2; 793 operand1 = p1; 794 } 795 } 796 797 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 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 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 887 888 public static class DateAndDuration extends ArithmeticExpression { 889 890 public DateAndDuration(Expression p1, int operator, Expression p2) { 891 892 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 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 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 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 | Popular Tags |