001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.metapath.function; // NOPMD - intentional 028 029import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; 030import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem; 031import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; 032import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; 033import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; 034import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem; 035import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem; 036import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem; 037import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; 038import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; 039import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem; 040 041import java.math.BigDecimal; 042import java.math.BigInteger; 043import java.time.Duration; 044import java.time.Period; 045import java.time.ZonedDateTime; 046import java.time.temporal.TemporalAmount; 047 048import edu.umd.cs.findbugs.annotations.NonNull; 049import edu.umd.cs.findbugs.annotations.Nullable; 050 051public final class OperationFunctions { // NOPMD - intentional 052 private OperationFunctions() { 053 // disable 054 } 055 056 @NonNull 057 public static IDateItem opAddYearMonthDurationToDate(@NonNull IDateItem arg1, @NonNull IYearMonthDurationItem arg2) { 058 return addDurationToDate(arg1.asZonedDateTime(), arg2.getValue()); 059 } 060 061 @NonNull 062 public static IDateItem opAddDayTimeDurationToDate(@NonNull IDateItem arg1, @NonNull IDayTimeDurationItem arg2) { 063 return addDurationToDate(arg1.asZonedDateTime(), arg2.getValue()); 064 } 065 066 @NonNull 067 private static IDateItem addDurationToDate(@NonNull ZonedDateTime dateTime, @NonNull TemporalAmount duration) { 068 ZonedDateTime result; 069 try { 070 result = dateTime.plus(duration); 071 } catch (ArithmeticException ex) { 072 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 073 } 074 assert result != null; 075 return IDateItem.valueOf(result); 076 } 077 078 @NonNull 079 public static IYearMonthDurationItem opAddYearMonthDurations(@NonNull IYearMonthDurationItem arg1, 080 IYearMonthDurationItem arg2) { 081 Period duration1 = arg1.getValue(); 082 Period duration2 = arg2.getValue(); 083 084 Period result; 085 try { 086 result = duration1.plus(duration2); 087 } catch (ArithmeticException ex) { 088 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 089 } 090 assert result != null; 091 return IYearMonthDurationItem.valueOf(result); 092 } 093 094 @NonNull 095 public static IDayTimeDurationItem opAddDayTimeDurations(@NonNull IDayTimeDurationItem arg1, 096 @NonNull IDayTimeDurationItem arg2) { 097 Duration duration1 = arg1.getValue(); 098 Duration duration2 = arg2.getValue(); 099 100 Duration result; 101 try { 102 result = duration1.plus(duration2); 103 } catch (ArithmeticException ex) { 104 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 105 } 106 assert result != null; 107 return IDayTimeDurationItem.valueOf(result); 108 } 109 110 @NonNull 111 public static IDateTimeItem opAddYearMonthDurationToDateTime(@NonNull IDateTimeItem arg1, 112 @NonNull IYearMonthDurationItem arg2) { 113 ZonedDateTime result; 114 try { 115 result = arg1.asZonedDateTime().plus(arg2.getValue()); 116 } catch (ArithmeticException ex) { 117 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 118 } 119 assert result != null; 120 return IDateTimeItem.valueOf(result); 121 } 122 123 @NonNull 124 public static IDateTimeItem opAddDayTimeDurationToDateTime(@NonNull IDateTimeItem arg1, 125 @NonNull IDayTimeDurationItem arg2) { 126 ZonedDateTime result; 127 try { 128 result = arg1.asZonedDateTime().plus(arg2.getValue()); 129 } catch (ArithmeticException ex) { 130 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 131 } 132 assert result != null; 133 return IDateTimeItem.valueOf(result); 134 } 135 136 @NonNull 137 public static IDayTimeDurationItem opSubtractDates(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { 138 return between(arg1.asZonedDateTime(), arg2.asZonedDateTime()); 139 } 140 141 @NonNull 142 public static IDateItem opSubtractYearMonthDurationFromDate(@NonNull IDateItem arg1, 143 @NonNull IYearMonthDurationItem arg2) { 144 return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.getValue()); 145 } 146 147 @NonNull 148 public static IDateItem opSubtractDayTimeDurationFromDate(@NonNull IDateItem arg1, 149 @NonNull IDayTimeDurationItem arg2) { 150 return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.getValue()); 151 } 152 153 @NonNull 154 private static IDateItem subtractDurationFromDate(@NonNull ZonedDateTime dateTime, 155 @NonNull TemporalAmount duration) { 156 @SuppressWarnings("null") 157 @NonNull ZonedDateTime result = dateTime.minus(duration); 158 return IDateItem.valueOf(result); 159 } 160 161 @NonNull 162 public static IYearMonthDurationItem opSubtractYearMonthDurations(@NonNull IYearMonthDurationItem arg1, 163 IYearMonthDurationItem arg2) { 164 Period duration1 = arg1.getValue(); 165 Period duration2 = arg2.getValue(); 166 167 @SuppressWarnings("null") 168 @NonNull Period duration = duration1.minus(duration2); 169 return IYearMonthDurationItem.valueOf(duration); 170 } 171 172 @NonNull 173 public static IDayTimeDurationItem opSubtractDayTimeDurations(@NonNull IDayTimeDurationItem arg1, 174 @NonNull IDayTimeDurationItem arg2) { 175 Duration duration1 = arg1.getValue(); 176 Duration duration2 = arg2.getValue(); 177 178 @SuppressWarnings("null") 179 @NonNull Duration duration = duration1.minus(duration2); 180 return IDayTimeDurationItem.valueOf(duration); 181 } 182 183 @NonNull 184 public static IDayTimeDurationItem opSubtractDateTimes(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { 185 return between(arg1.asZonedDateTime(), arg2.asZonedDateTime()); 186 } 187 188 @NonNull 189 private static IDayTimeDurationItem between(@NonNull ZonedDateTime time1, @NonNull ZonedDateTime time2) { 190 @SuppressWarnings("null") 191 @NonNull Duration between = Duration.between(time1, time2); 192 return IDayTimeDurationItem.valueOf(between); 193 } 194 195 @NonNull 196 public static IDateTimeItem opSubtractYearMonthDurationFromDateTime(@NonNull IDateTimeItem arg1, 197 @NonNull IYearMonthDurationItem arg2) { 198 @SuppressWarnings("null") 199 @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().minus(arg2.getValue()); 200 return IDateTimeItem.valueOf(dateTime); 201 } 202 203 @NonNull 204 public static IDateTimeItem opSubtractDayTimeDurationFromDateTime(@NonNull IDateTimeItem arg1, 205 @NonNull IDayTimeDurationItem arg2) { 206 207 @SuppressWarnings("null") 208 @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().plus(arg2.getValue()); 209 return IDateTimeItem.valueOf(dateTime); 210 } 211 212 @NonNull 213 public static IYearMonthDurationItem opMultiplyYearMonthDuration(@NonNull IYearMonthDurationItem arg1, 214 @NonNull INumericItem arg2) 215 throws ArithmeticFunctionException { 216 int arg2Int; 217 try { 218 arg2Int = FunctionUtils.asInteger(arg2.round()); 219 } catch (ArithmeticException ex) { 220 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 221 } 222 223 @SuppressWarnings("null") 224 @NonNull Period period = arg1.getValue().multipliedBy(arg2Int); 225 return IYearMonthDurationItem.valueOf(period); 226 } 227 228 @NonNull 229 public static IDayTimeDurationItem opMultiplyDayTimeDuration(@NonNull IDayTimeDurationItem arg1, 230 @NonNull INumericItem arg2) 231 throws ArithmeticFunctionException { 232 long arg2Long; 233 try { 234 arg2Long = FunctionUtils.asLong(arg2.round()); 235 } catch (ArithmeticException ex) { 236 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); 237 } 238 239 @SuppressWarnings("null") 240 @NonNull Duration duration = arg1.getValue().multipliedBy(arg2Long); 241 return IDayTimeDurationItem.valueOf(duration); 242 } 243 244 @NonNull 245 public static IYearMonthDurationItem opDivideYearMonthDuration(@NonNull IYearMonthDurationItem arg1, 246 @NonNull INumericItem arg2) 247 throws DateTimeFunctionException { 248 IIntegerItem totalMonths = IIntegerItem.valueOf(arg1.getValue().toTotalMonths()); 249 IIntegerItem result = opNumericIntegerDivide(totalMonths, arg2); 250 int months; 251 try { 252 months = FunctionUtils.asInteger(result.asInteger()); 253 } catch (ArithmeticException ex) { 254 throw new DateTimeFunctionException(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, 255 "Overflow/underflow in duration operation.", ex); 256 } 257 int years = months / 12; 258 months = months % 12; 259 return IYearMonthDurationItem.valueOf(years, months, 0); 260 } 261 262 @NonNull 263 public static IDayTimeDurationItem opDivideDayTimeDuration(@NonNull IDayTimeDurationItem arg1, 264 @NonNull INumericItem arg2) 265 throws ArithmeticFunctionException { 266 try { 267 @SuppressWarnings("null") 268 @NonNull Duration duration = arg1.getValue().dividedBy(FunctionUtils.asLong(arg2.round())); 269 return IDayTimeDurationItem 270 .valueOf(duration); 271 } catch (ArithmeticException ex) { 272 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, "Division by zero", ex); 273 } 274 } 275 276 @NonNull 277 public static IDecimalItem opDivideDayTimeDurationByDayTimeDuration(@NonNull IDayTimeDurationItem arg1, 278 IDayTimeDurationItem arg2) { 279 return IDecimalItem.cast( 280 opNumericDivide( 281 IDecimalItem.valueOf(arg1.getValue().toSeconds()), 282 IDecimalItem.valueOf(arg2.getValue().toSeconds()))); 283 } 284 285 @NonNull 286 public static IBooleanItem opDateEqual(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { 287 return opDateTimeEqual(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); 288 } 289 290 @NonNull 291 public static IBooleanItem opDateTimeEqual(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { 292 return IBooleanItem.valueOf(arg1.asZonedDateTime().equals(arg2.asZonedDateTime())); 293 } 294 295 @NonNull 296 public static IBooleanItem opDurationEqual(@NonNull IDurationItem arg1, @NonNull IDurationItem arg2) { 297 return IBooleanItem.valueOf(arg1.getValue().equals(arg2.getValue())); 298 } 299 300 @NonNull 301 public static IBooleanItem opBase64BinaryEqual(@NonNull IBase64BinaryItem arg1, @NonNull IBase64BinaryItem arg2) { 302 return IBooleanItem.valueOf(arg1.getValue().equals(arg2.getValue())); 303 } 304 305 @NonNull 306 public static IBooleanItem opDateGreaterThan(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { 307 return opDateTimeGreaterThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); 308 } 309 310 @NonNull 311 public static IBooleanItem opDateTimeGreaterThan(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { 312 return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) > 0); 313 } 314 315 @NonNull 316 public static IBooleanItem opYearMonthDurationGreaterThan(@NonNull IYearMonthDurationItem arg1, 317 @NonNull IYearMonthDurationItem arg2) { 318 Period p1 = arg1.getValue(); 319 Period p2 = arg2.getValue(); 320 321 // this is only an approximation 322 return IBooleanItem.valueOf(p1.toTotalMonths() > p2.toTotalMonths()); 323 } 324 325 @NonNull 326 public static IBooleanItem opDayTimeDurationGreaterThan( 327 @NonNull IDayTimeDurationItem arg1, 328 @NonNull IDayTimeDurationItem arg2) { 329 return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) > 0); 330 } 331 332 @NonNull 333 public static IBooleanItem opBase64BinaryGreaterThan( 334 @NonNull IBase64BinaryItem arg1, 335 @NonNull IBase64BinaryItem arg2) { 336 return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) > 0); 337 } 338 339 @NonNull 340 public static IBooleanItem opDateLessThan( 341 @NonNull IDateItem arg1, 342 @NonNull IDateItem arg2) { 343 return opDateTimeLessThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); 344 } 345 346 @NonNull 347 public static IBooleanItem opDateTimeLessThan( 348 @NonNull IDateTimeItem arg1, 349 @NonNull IDateTimeItem arg2) { 350 return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) < 0); 351 } 352 353 @NonNull 354 public static IBooleanItem opYearMonthDurationLessThan(@NonNull IYearMonthDurationItem arg1, 355 @NonNull IYearMonthDurationItem arg2) { 356 Period p1 = arg1.getValue(); 357 Period p2 = arg2.getValue(); 358 359 // this is only an approximation 360 return IBooleanItem.valueOf(p1.toTotalMonths() < p2.toTotalMonths()); 361 } 362 363 @NonNull 364 public static IBooleanItem opDayTimeDurationLessThan( 365 @NonNull IDayTimeDurationItem arg1, 366 @NonNull IDayTimeDurationItem arg2) { 367 return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) < 0); 368 } 369 370 @NonNull 371 public static IBooleanItem opBase64BinaryLessThan( 372 @NonNull IBase64BinaryItem arg1, 373 @NonNull IBase64BinaryItem arg2) { 374 return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) < 0); 375 } 376 377 @NonNull 378 public static INumericItem opNumericAdd(@NonNull INumericItem left, @NonNull INumericItem right) { 379 INumericItem retval; 380 if (left instanceof IDecimalItem || right instanceof IDecimalItem) { 381 // create a decimal result 382 BigDecimal decimalLeft = left.asDecimal(); 383 BigDecimal decimalRight = right.asDecimal(); 384 385 @SuppressWarnings("null") 386 @NonNull BigDecimal result = decimalLeft.add(decimalRight, FunctionUtils.MATH_CONTEXT); 387 retval = IDecimalItem.valueOf(result); 388 } else { 389 // create an integer result 390 BigInteger integerLeft = left.asInteger(); 391 BigInteger integerRight = right.asInteger(); 392 393 @SuppressWarnings("null") 394 @NonNull BigInteger result = integerLeft.add(integerRight); 395 retval = IIntegerItem.valueOf(result); 396 } 397 return retval; 398 } 399 400 @NonNull 401 public static INumericItem opNumericSubtract(@NonNull INumericItem left, @NonNull INumericItem right) { 402 INumericItem retval; 403 if (left instanceof IDecimalItem || right instanceof IDecimalItem) { 404 // create a decimal result 405 BigDecimal decimalLeft = left.asDecimal(); 406 BigDecimal decimalRight = right.asDecimal(); 407 408 @SuppressWarnings("null") 409 @NonNull BigDecimal result = decimalLeft.subtract(decimalRight, FunctionUtils.MATH_CONTEXT); 410 retval = IDecimalItem.valueOf(result); 411 } else { 412 // create an integer result 413 BigInteger integerLeft = left.asInteger(); 414 BigInteger integerRight = right.asInteger(); 415 416 @SuppressWarnings("null") 417 @NonNull BigInteger result = integerLeft.subtract(integerRight); 418 retval = IIntegerItem.valueOf(result); 419 } 420 return retval; 421 } 422 423 @NonNull 424 public static INumericItem opNumericMultiply(@NonNull INumericItem left, @NonNull INumericItem right) { 425 INumericItem retval; 426 if (left instanceof IDecimalItem || right instanceof IDecimalItem) { 427 // create a decimal result 428 BigDecimal decimalLeft = left.asDecimal(); 429 BigDecimal decimalRight = right.asDecimal(); 430 431 @SuppressWarnings("null") 432 @NonNull BigDecimal result = decimalLeft.multiply(decimalRight, FunctionUtils.MATH_CONTEXT); 433 retval = IDecimalItem.valueOf(result); 434 } else { 435 // create an integer result 436 @SuppressWarnings("null") 437 @NonNull BigInteger result = left.asInteger().multiply(right.asInteger()); 438 retval = IIntegerItem.valueOf(result); 439 } 440 return retval; 441 } 442 443 @NonNull 444 public static INumericItem opNumericDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) { 445 INumericItem retval; 446 if (dividend instanceof IDecimalItem || divisor instanceof IDecimalItem) { 447 // create a decimal result 448 BigDecimal decimalDivisor = divisor.asDecimal(); 449 450 if (BigDecimal.ZERO.equals(decimalDivisor)) { 451 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, 452 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE); 453 } 454 455 BigDecimal decimalDividend = dividend.asDecimal(); 456 457 @SuppressWarnings("null") 458 @NonNull BigDecimal result = decimalDividend.divide(decimalDivisor, FunctionUtils.MATH_CONTEXT); 459 retval = IDecimalItem.valueOf(result); 460 } else { 461 // create an integer result 462 BigInteger integerDivisor = divisor.asInteger(); 463 464 if (BigInteger.ZERO.equals(integerDivisor)) { 465 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, 466 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE); 467 } 468 469 BigInteger integerDividend = dividend.asInteger(); 470 471 @SuppressWarnings("null") 472 @NonNull BigInteger result = integerDividend.divide(integerDivisor); 473 retval = IIntegerItem.valueOf(result); 474 } 475 return retval; 476 } 477 478 @NonNull 479 public static IIntegerItem opNumericIntegerDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) { 480 IIntegerItem retval; 481 if (dividend instanceof IDecimalItem || divisor instanceof IDecimalItem) { 482 // create a decimal result 483 BigDecimal decimalDivisor = divisor.asDecimal(); 484 485 if (BigDecimal.ZERO.equals(decimalDivisor)) { 486 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, 487 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE); 488 } 489 490 BigDecimal decimalDividend = dividend.asDecimal(); 491 492 @SuppressWarnings("null") 493 @NonNull BigInteger result 494 = decimalDividend.divideToIntegralValue(decimalDivisor, FunctionUtils.MATH_CONTEXT).toBigInteger(); 495 retval = IIntegerItem.valueOf(result); 496 } else { 497 // create an integer result 498 BigInteger integerDivisor = divisor.asInteger(); 499 500 if (BigInteger.ZERO.equals(integerDivisor)) { 501 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, 502 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE); 503 } 504 505 @SuppressWarnings("null") 506 @NonNull BigInteger result = dividend.asInteger().divide(integerDivisor); 507 retval = IIntegerItem.valueOf(result); 508 } 509 return retval; 510 } 511 512 /** 513 * Based on XPath 3.1 <a href= 514 * "https://www.w3.org/TR/xpath-functions-31/#func-numeric-mod">func:numeric-mod</a>. 515 * 516 * @param dividend 517 * the number to be divided 518 * @param divisor 519 * the number to divide by 520 * @return the remainder 521 */ 522 @NonNull 523 public static INumericItem opNumericMod(@NonNull INumericItem dividend, @NonNull INumericItem divisor) { 524 BigDecimal decimalDivisor = divisor.asDecimal(); 525 526 if (BigDecimal.ZERO.equals(decimalDivisor)) { 527 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, 528 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE); 529 } 530 531 BigDecimal decimalDividend = dividend.asDecimal(); 532 533 INumericItem retval; 534 if (BigDecimal.ZERO.equals(decimalDividend)) { 535 retval = dividend; 536 } else { 537 @SuppressWarnings("null") 538 @NonNull BigDecimal result = decimalDividend.remainder(decimalDivisor, FunctionUtils.MATH_CONTEXT); 539 retval = IDecimalItem.valueOf(result); 540 } 541 return retval; 542 } 543 544 @NonNull 545 public static INumericItem opNumericUnaryMinus(@NonNull INumericItem item) { 546 INumericItem retval; 547 if (item instanceof IDecimalItem) { 548 // create a decimal result 549 BigDecimal decimal = item.asDecimal(); 550 551 @SuppressWarnings("null") 552 @NonNull BigDecimal result = decimal.negate(FunctionUtils.MATH_CONTEXT); 553 retval = IDecimalItem.valueOf(result); 554 } else if (item instanceof IIntegerItem) { 555 // create a decimal result 556 BigInteger integer = item.asInteger(); 557 558 @SuppressWarnings("null") 559 @NonNull BigInteger result = integer.negate(); 560 retval = IIntegerItem.valueOf(result); 561 } else { 562 throw new InvalidTypeMetapathException(item); 563 } 564 return retval; 565 } 566 567 @NonNull 568 public static IBooleanItem opNumericEqual(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { 569 IBooleanItem retval; 570 if (arg1 == null || arg2 == null) { 571 retval = IBooleanItem.FALSE; 572 } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) { 573 retval = IBooleanItem.valueOf(arg1.asDecimal().equals(arg2.asDecimal())); 574 } else { 575 retval = IBooleanItem.valueOf(arg1.asInteger().equals(arg2.asInteger())); 576 } 577 return retval; 578 } 579 580 @NonNull 581 public static IBooleanItem opNumericGreaterThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { 582 IBooleanItem retval; 583 if (arg1 == null || arg2 == null) { 584 retval = IBooleanItem.FALSE; 585 } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) { 586 int result = arg1.asDecimal().compareTo(arg2.asDecimal()); 587 retval = IBooleanItem.valueOf(result > 0); 588 } else { 589 int result = arg1.asInteger().compareTo(arg2.asInteger()); 590 retval = IBooleanItem.valueOf(result > 0); 591 } 592 return retval; 593 } 594 595 @NonNull 596 public static IBooleanItem opNumericLessThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { 597 IBooleanItem retval; 598 if (arg1 == null || arg2 == null) { 599 retval = IBooleanItem.FALSE; 600 } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) { 601 int result = arg1.asDecimal().compareTo(arg2.asDecimal()); 602 retval = IBooleanItem.valueOf(result < 0); 603 } else { 604 int result = arg1.asInteger().compareTo(arg2.asInteger()); 605 retval = IBooleanItem.valueOf(result < 0); 606 } 607 return retval; 608 } 609 610 @NonNull 611 public static IBooleanItem opBooleanEqual(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { 612 boolean left = arg1 != null && arg1.toBoolean(); 613 boolean right = arg2 != null && arg2.toBoolean(); 614 615 return IBooleanItem.valueOf(left == right); 616 } 617 618 @NonNull 619 public static IBooleanItem opBooleanGreaterThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { 620 boolean left = arg1 != null && arg1.toBoolean(); 621 boolean right = arg2 != null && arg2.toBoolean(); 622 623 return IBooleanItem.valueOf(left && !right); 624 } 625 626 @NonNull 627 public static IBooleanItem opBooleanLessThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { 628 boolean left = arg1 != null && arg1.toBoolean(); 629 boolean right = arg2 != null && arg2.toBoolean(); 630 631 return IBooleanItem.valueOf(!left && right); 632 } 633}