OperationFunctions.java

  1. /*
  2.  * Portions of this software was developed by employees of the National Institute
  3.  * of Standards and Technology (NIST), an agency of the Federal Government and is
  4.  * being made available as a public service. Pursuant to title 17 United States
  5.  * Code Section 105, works of NIST employees are not subject to copyright
  6.  * protection in the United States. This software may be subject to foreign
  7.  * copyright. Permission in the United States and in foreign countries, to the
  8.  * extent that NIST may hold copyright, to use, copy, modify, create derivative
  9.  * works, and distribute this software and its documentation without fee is hereby
  10.  * granted on a non-exclusive basis, provided that this notice and disclaimer
  11.  * of warranty appears in all copies.
  12.  *
  13.  * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
  14.  * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
  15.  * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
  16.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
  17.  * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
  18.  * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
  19.  * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
  20.  * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
  21.  * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
  22.  * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
  23.  * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
  24.  * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
  25.  */

  26. package gov.nist.secauto.metaschema.core.metapath.function; // NOPMD - intentional

  27. import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException;
  28. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
  29. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
  30. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
  31. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem;
  32. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
  33. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem;
  34. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
  35. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
  36. import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
  37. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;

  38. import java.math.BigDecimal;
  39. import java.math.BigInteger;
  40. import java.time.Duration;
  41. import java.time.Period;
  42. import java.time.ZonedDateTime;
  43. import java.time.temporal.TemporalAmount;

  44. import edu.umd.cs.findbugs.annotations.NonNull;
  45. import edu.umd.cs.findbugs.annotations.Nullable;

  46. public final class OperationFunctions { // NOPMD - intentional
  47.   private OperationFunctions() {
  48.     // disable
  49.   }

  50.   @NonNull
  51.   public static IDateItem opAddYearMonthDurationToDate(@NonNull IDateItem arg1, @NonNull IYearMonthDurationItem arg2) {
  52.     return addDurationToDate(arg1.asZonedDateTime(), arg2.getValue());
  53.   }

  54.   @NonNull
  55.   public static IDateItem opAddDayTimeDurationToDate(@NonNull IDateItem arg1, @NonNull IDayTimeDurationItem arg2) {
  56.     return addDurationToDate(arg1.asZonedDateTime(), arg2.getValue());
  57.   }

  58.   @NonNull
  59.   private static IDateItem addDurationToDate(@NonNull ZonedDateTime dateTime, @NonNull TemporalAmount duration) {
  60.     ZonedDateTime result;
  61.     try {
  62.       result = dateTime.plus(duration);
  63.     } catch (ArithmeticException ex) {
  64.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  65.     }
  66.     assert result != null;
  67.     return IDateItem.valueOf(result);
  68.   }

  69.   @NonNull
  70.   public static IYearMonthDurationItem opAddYearMonthDurations(@NonNull IYearMonthDurationItem arg1,
  71.       IYearMonthDurationItem arg2) {
  72.     Period duration1 = arg1.getValue();
  73.     Period duration2 = arg2.getValue();

  74.     Period result;
  75.     try {
  76.       result = duration1.plus(duration2);
  77.     } catch (ArithmeticException ex) {
  78.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  79.     }
  80.     assert result != null;
  81.     return IYearMonthDurationItem.valueOf(result);
  82.   }

  83.   @NonNull
  84.   public static IDayTimeDurationItem opAddDayTimeDurations(@NonNull IDayTimeDurationItem arg1,
  85.       @NonNull IDayTimeDurationItem arg2) {
  86.     Duration duration1 = arg1.getValue();
  87.     Duration duration2 = arg2.getValue();

  88.     Duration result;
  89.     try {
  90.       result = duration1.plus(duration2);
  91.     } catch (ArithmeticException ex) {
  92.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  93.     }
  94.     assert result != null;
  95.     return IDayTimeDurationItem.valueOf(result);
  96.   }

  97.   @NonNull
  98.   public static IDateTimeItem opAddYearMonthDurationToDateTime(@NonNull IDateTimeItem arg1,
  99.       @NonNull IYearMonthDurationItem arg2) {
  100.     ZonedDateTime result;
  101.     try {
  102.       result = arg1.asZonedDateTime().plus(arg2.getValue());
  103.     } catch (ArithmeticException ex) {
  104.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  105.     }
  106.     assert result != null;
  107.     return IDateTimeItem.valueOf(result);
  108.   }

  109.   @NonNull
  110.   public static IDateTimeItem opAddDayTimeDurationToDateTime(@NonNull IDateTimeItem arg1,
  111.       @NonNull IDayTimeDurationItem arg2) {
  112.     ZonedDateTime result;
  113.     try {
  114.       result = arg1.asZonedDateTime().plus(arg2.getValue());
  115.     } catch (ArithmeticException ex) {
  116.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  117.     }
  118.     assert result != null;
  119.     return IDateTimeItem.valueOf(result);
  120.   }

  121.   @NonNull
  122.   public static IDayTimeDurationItem opSubtractDates(@NonNull IDateItem arg1, @NonNull IDateItem arg2) {
  123.     return between(arg1.asZonedDateTime(), arg2.asZonedDateTime());
  124.   }

  125.   @NonNull
  126.   public static IDateItem opSubtractYearMonthDurationFromDate(@NonNull IDateItem arg1,
  127.       @NonNull IYearMonthDurationItem arg2) {
  128.     return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.getValue());
  129.   }

  130.   @NonNull
  131.   public static IDateItem opSubtractDayTimeDurationFromDate(@NonNull IDateItem arg1,
  132.       @NonNull IDayTimeDurationItem arg2) {
  133.     return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.getValue());
  134.   }

  135.   @NonNull
  136.   private static IDateItem subtractDurationFromDate(@NonNull ZonedDateTime dateTime,
  137.       @NonNull TemporalAmount duration) {
  138.     @SuppressWarnings("null")
  139.     @NonNull ZonedDateTime result = dateTime.minus(duration);
  140.     return IDateItem.valueOf(result);
  141.   }

  142.   @NonNull
  143.   public static IYearMonthDurationItem opSubtractYearMonthDurations(@NonNull IYearMonthDurationItem arg1,
  144.       IYearMonthDurationItem arg2) {
  145.     Period duration1 = arg1.getValue();
  146.     Period duration2 = arg2.getValue();

  147.     @SuppressWarnings("null")
  148.     @NonNull Period duration = duration1.minus(duration2);
  149.     return IYearMonthDurationItem.valueOf(duration);
  150.   }

  151.   @NonNull
  152.   public static IDayTimeDurationItem opSubtractDayTimeDurations(@NonNull IDayTimeDurationItem arg1,
  153.       @NonNull IDayTimeDurationItem arg2) {
  154.     Duration duration1 = arg1.getValue();
  155.     Duration duration2 = arg2.getValue();

  156.     @SuppressWarnings("null")
  157.     @NonNull Duration duration = duration1.minus(duration2);
  158.     return IDayTimeDurationItem.valueOf(duration);
  159.   }

  160.   @NonNull
  161.   public static IDayTimeDurationItem opSubtractDateTimes(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) {
  162.     return between(arg1.asZonedDateTime(), arg2.asZonedDateTime());
  163.   }

  164.   @NonNull
  165.   private static IDayTimeDurationItem between(@NonNull ZonedDateTime time1, @NonNull ZonedDateTime time2) {
  166.     @SuppressWarnings("null")
  167.     @NonNull Duration between = Duration.between(time1, time2);
  168.     return IDayTimeDurationItem.valueOf(between);
  169.   }

  170.   @NonNull
  171.   public static IDateTimeItem opSubtractYearMonthDurationFromDateTime(@NonNull IDateTimeItem arg1,
  172.       @NonNull IYearMonthDurationItem arg2) {
  173.     @SuppressWarnings("null")
  174.     @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().minus(arg2.getValue());
  175.     return IDateTimeItem.valueOf(dateTime);
  176.   }

  177.   @NonNull
  178.   public static IDateTimeItem opSubtractDayTimeDurationFromDateTime(@NonNull IDateTimeItem arg1,
  179.       @NonNull IDayTimeDurationItem arg2) {

  180.     @SuppressWarnings("null")
  181.     @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().plus(arg2.getValue());
  182.     return IDateTimeItem.valueOf(dateTime);
  183.   }

  184.   @NonNull
  185.   public static IYearMonthDurationItem opMultiplyYearMonthDuration(@NonNull IYearMonthDurationItem arg1,
  186.       @NonNull INumericItem arg2)
  187.       throws ArithmeticFunctionException {
  188.     int arg2Int;
  189.     try {
  190.       arg2Int = FunctionUtils.asInteger(arg2.round());
  191.     } catch (ArithmeticException ex) {
  192.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  193.     }

  194.     @SuppressWarnings("null")
  195.     @NonNull Period period = arg1.getValue().multipliedBy(arg2Int);
  196.     return IYearMonthDurationItem.valueOf(period);
  197.   }

  198.   @NonNull
  199.   public static IDayTimeDurationItem opMultiplyDayTimeDuration(@NonNull IDayTimeDurationItem arg1,
  200.       @NonNull INumericItem arg2)
  201.       throws ArithmeticFunctionException {
  202.     long arg2Long;
  203.     try {
  204.       arg2Long = FunctionUtils.asLong(arg2.round());
  205.     } catch (ArithmeticException ex) {
  206.       throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex);
  207.     }

  208.     @SuppressWarnings("null")
  209.     @NonNull Duration duration = arg1.getValue().multipliedBy(arg2Long);
  210.     return IDayTimeDurationItem.valueOf(duration);
  211.   }

  212.   @NonNull
  213.   public static IYearMonthDurationItem opDivideYearMonthDuration(@NonNull IYearMonthDurationItem arg1,
  214.       @NonNull INumericItem arg2)
  215.       throws DateTimeFunctionException {
  216.     IIntegerItem totalMonths = IIntegerItem.valueOf(arg1.getValue().toTotalMonths());
  217.     IIntegerItem result = opNumericIntegerDivide(totalMonths, arg2);
  218.     int months;
  219.     try {
  220.       months = FunctionUtils.asInteger(result.asInteger());
  221.     } catch (ArithmeticException ex) {
  222.       throw new DateTimeFunctionException(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR,
  223.           "Overflow/underflow in duration operation.", ex);
  224.     }
  225.     int years = months / 12;
  226.     months = months % 12;
  227.     return IYearMonthDurationItem.valueOf(years, months, 0);
  228.   }

  229.   @NonNull
  230.   public static IDayTimeDurationItem opDivideDayTimeDuration(@NonNull IDayTimeDurationItem arg1,
  231.       @NonNull INumericItem arg2)
  232.       throws ArithmeticFunctionException {
  233.     try {
  234.       @SuppressWarnings("null")
  235.       @NonNull Duration duration = arg1.getValue().dividedBy(FunctionUtils.asLong(arg2.round()));
  236.       return IDayTimeDurationItem
  237.           .valueOf(duration);
  238.     } catch (ArithmeticException ex) {
  239.       throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, "Division by zero", ex);
  240.     }
  241.   }

  242.   @NonNull
  243.   public static IDecimalItem opDivideDayTimeDurationByDayTimeDuration(@NonNull IDayTimeDurationItem arg1,
  244.       IDayTimeDurationItem arg2) {
  245.     return IDecimalItem.cast(
  246.         opNumericDivide(
  247.             IDecimalItem.valueOf(arg1.getValue().toSeconds()),
  248.             IDecimalItem.valueOf(arg2.getValue().toSeconds())));
  249.   }

  250.   @NonNull
  251.   public static IBooleanItem opDateEqual(@NonNull IDateItem arg1, @NonNull IDateItem arg2) {
  252.     return opDateTimeEqual(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2));
  253.   }

  254.   @NonNull
  255.   public static IBooleanItem opDateTimeEqual(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) {
  256.     return IBooleanItem.valueOf(arg1.asZonedDateTime().equals(arg2.asZonedDateTime()));
  257.   }

  258.   @NonNull
  259.   public static IBooleanItem opDurationEqual(@NonNull IDurationItem arg1, @NonNull IDurationItem arg2) {
  260.     return IBooleanItem.valueOf(arg1.getValue().equals(arg2.getValue()));
  261.   }

  262.   @NonNull
  263.   public static IBooleanItem opBase64BinaryEqual(@NonNull IBase64BinaryItem arg1, @NonNull IBase64BinaryItem arg2) {
  264.     return IBooleanItem.valueOf(arg1.getValue().equals(arg2.getValue()));
  265.   }

  266.   @NonNull
  267.   public static IBooleanItem opDateGreaterThan(@NonNull IDateItem arg1, @NonNull IDateItem arg2) {
  268.     return opDateTimeGreaterThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2));
  269.   }

  270.   @NonNull
  271.   public static IBooleanItem opDateTimeGreaterThan(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) {
  272.     return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) > 0);
  273.   }

  274.   @NonNull
  275.   public static IBooleanItem opYearMonthDurationGreaterThan(@NonNull IYearMonthDurationItem arg1,
  276.       @NonNull IYearMonthDurationItem arg2) {
  277.     Period p1 = arg1.getValue();
  278.     Period p2 = arg2.getValue();

  279.     // this is only an approximation
  280.     return IBooleanItem.valueOf(p1.toTotalMonths() > p2.toTotalMonths());
  281.   }

  282.   @NonNull
  283.   public static IBooleanItem opDayTimeDurationGreaterThan(
  284.       @NonNull IDayTimeDurationItem arg1,
  285.       @NonNull IDayTimeDurationItem arg2) {
  286.     return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) > 0);
  287.   }

  288.   @NonNull
  289.   public static IBooleanItem opBase64BinaryGreaterThan(
  290.       @NonNull IBase64BinaryItem arg1,
  291.       @NonNull IBase64BinaryItem arg2) {
  292.     return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) > 0);
  293.   }

  294.   @NonNull
  295.   public static IBooleanItem opDateLessThan(
  296.       @NonNull IDateItem arg1,
  297.       @NonNull IDateItem arg2) {
  298.     return opDateTimeLessThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2));
  299.   }

  300.   @NonNull
  301.   public static IBooleanItem opDateTimeLessThan(
  302.       @NonNull IDateTimeItem arg1,
  303.       @NonNull IDateTimeItem arg2) {
  304.     return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) < 0);
  305.   }

  306.   @NonNull
  307.   public static IBooleanItem opYearMonthDurationLessThan(@NonNull IYearMonthDurationItem arg1,
  308.       @NonNull IYearMonthDurationItem arg2) {
  309.     Period p1 = arg1.getValue();
  310.     Period p2 = arg2.getValue();

  311.     // this is only an approximation
  312.     return IBooleanItem.valueOf(p1.toTotalMonths() < p2.toTotalMonths());
  313.   }

  314.   @NonNull
  315.   public static IBooleanItem opDayTimeDurationLessThan(
  316.       @NonNull IDayTimeDurationItem arg1,
  317.       @NonNull IDayTimeDurationItem arg2) {
  318.     return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) < 0);
  319.   }

  320.   @NonNull
  321.   public static IBooleanItem opBase64BinaryLessThan(
  322.       @NonNull IBase64BinaryItem arg1,
  323.       @NonNull IBase64BinaryItem arg2) {
  324.     return IBooleanItem.valueOf(arg1.getValue().compareTo(arg2.getValue()) < 0);
  325.   }

  326.   @NonNull
  327.   public static INumericItem opNumericAdd(@NonNull INumericItem left, @NonNull INumericItem right) {
  328.     INumericItem retval;
  329.     if (left instanceof IDecimalItem || right instanceof IDecimalItem) {
  330.       // create a decimal result
  331.       BigDecimal decimalLeft = left.asDecimal();
  332.       BigDecimal decimalRight = right.asDecimal();

  333.       @SuppressWarnings("null")
  334.       @NonNull BigDecimal result = decimalLeft.add(decimalRight, FunctionUtils.MATH_CONTEXT);
  335.       retval = IDecimalItem.valueOf(result);
  336.     } else {
  337.       // create an integer result
  338.       BigInteger integerLeft = left.asInteger();
  339.       BigInteger integerRight = right.asInteger();

  340.       @SuppressWarnings("null")
  341.       @NonNull BigInteger result = integerLeft.add(integerRight);
  342.       retval = IIntegerItem.valueOf(result);
  343.     }
  344.     return retval;
  345.   }

  346.   @NonNull
  347.   public static INumericItem opNumericSubtract(@NonNull INumericItem left, @NonNull INumericItem right) {
  348.     INumericItem retval;
  349.     if (left instanceof IDecimalItem || right instanceof IDecimalItem) {
  350.       // create a decimal result
  351.       BigDecimal decimalLeft = left.asDecimal();
  352.       BigDecimal decimalRight = right.asDecimal();

  353.       @SuppressWarnings("null")
  354.       @NonNull BigDecimal result = decimalLeft.subtract(decimalRight, FunctionUtils.MATH_CONTEXT);
  355.       retval = IDecimalItem.valueOf(result);
  356.     } else {
  357.       // create an integer result
  358.       BigInteger integerLeft = left.asInteger();
  359.       BigInteger integerRight = right.asInteger();

  360.       @SuppressWarnings("null")
  361.       @NonNull BigInteger result = integerLeft.subtract(integerRight);
  362.       retval = IIntegerItem.valueOf(result);
  363.     }
  364.     return retval;
  365.   }

  366.   @NonNull
  367.   public static INumericItem opNumericMultiply(@NonNull INumericItem left, @NonNull INumericItem right) {
  368.     INumericItem retval;
  369.     if (left instanceof IDecimalItem || right instanceof IDecimalItem) {
  370.       // create a decimal result
  371.       BigDecimal decimalLeft = left.asDecimal();
  372.       BigDecimal decimalRight = right.asDecimal();

  373.       @SuppressWarnings("null")
  374.       @NonNull BigDecimal result = decimalLeft.multiply(decimalRight, FunctionUtils.MATH_CONTEXT);
  375.       retval = IDecimalItem.valueOf(result);
  376.     } else {
  377.       // create an integer result
  378.       @SuppressWarnings("null")
  379.       @NonNull BigInteger result = left.asInteger().multiply(right.asInteger());
  380.       retval = IIntegerItem.valueOf(result);
  381.     }
  382.     return retval;
  383.   }

  384.   @NonNull
  385.   public static INumericItem opNumericDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) {
  386.     INumericItem retval;
  387.     if (dividend instanceof IDecimalItem || divisor instanceof IDecimalItem) {
  388.       // create a decimal result
  389.       BigDecimal decimalDivisor = divisor.asDecimal();

  390.       if (BigDecimal.ZERO.equals(decimalDivisor)) {
  391.         throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
  392.             ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
  393.       }

  394.       BigDecimal decimalDividend = dividend.asDecimal();

  395.       @SuppressWarnings("null")
  396.       @NonNull BigDecimal result = decimalDividend.divide(decimalDivisor, FunctionUtils.MATH_CONTEXT);
  397.       retval = IDecimalItem.valueOf(result);
  398.     } else {
  399.       // create an integer result
  400.       BigInteger integerDivisor = divisor.asInteger();

  401.       if (BigInteger.ZERO.equals(integerDivisor)) {
  402.         throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
  403.             ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
  404.       }

  405.       BigInteger integerDividend = dividend.asInteger();

  406.       @SuppressWarnings("null")
  407.       @NonNull BigInteger result = integerDividend.divide(integerDivisor);
  408.       retval = IIntegerItem.valueOf(result);
  409.     }
  410.     return retval;
  411.   }

  412.   @NonNull
  413.   public static IIntegerItem opNumericIntegerDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) {
  414.     IIntegerItem retval;
  415.     if (dividend instanceof IDecimalItem || divisor instanceof IDecimalItem) {
  416.       // create a decimal result
  417.       BigDecimal decimalDivisor = divisor.asDecimal();

  418.       if (BigDecimal.ZERO.equals(decimalDivisor)) {
  419.         throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
  420.             ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
  421.       }

  422.       BigDecimal decimalDividend = dividend.asDecimal();

  423.       @SuppressWarnings("null")
  424.       @NonNull BigInteger result
  425.           = decimalDividend.divideToIntegralValue(decimalDivisor, FunctionUtils.MATH_CONTEXT).toBigInteger();
  426.       retval = IIntegerItem.valueOf(result);
  427.     } else {
  428.       // create an integer result
  429.       BigInteger integerDivisor = divisor.asInteger();

  430.       if (BigInteger.ZERO.equals(integerDivisor)) {
  431.         throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
  432.             ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
  433.       }

  434.       @SuppressWarnings("null")
  435.       @NonNull BigInteger result = dividend.asInteger().divide(integerDivisor);
  436.       retval = IIntegerItem.valueOf(result);
  437.     }
  438.     return retval;
  439.   }

  440.   /**
  441.    * Based on XPath 3.1 <a href=
  442.    * "https://www.w3.org/TR/xpath-functions-31/#func-numeric-mod">func:numeric-mod</a>.
  443.    *
  444.    * @param dividend
  445.    *          the number to be divided
  446.    * @param divisor
  447.    *          the number to divide by
  448.    * @return the remainder
  449.    */
  450.   @NonNull
  451.   public static INumericItem opNumericMod(@NonNull INumericItem dividend, @NonNull INumericItem divisor) {
  452.     BigDecimal decimalDivisor = divisor.asDecimal();

  453.     if (BigDecimal.ZERO.equals(decimalDivisor)) {
  454.       throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
  455.           ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
  456.     }

  457.     BigDecimal decimalDividend = dividend.asDecimal();

  458.     INumericItem retval;
  459.     if (BigDecimal.ZERO.equals(decimalDividend)) {
  460.       retval = dividend;
  461.     } else {
  462.       @SuppressWarnings("null")
  463.       @NonNull BigDecimal result = decimalDividend.remainder(decimalDivisor, FunctionUtils.MATH_CONTEXT);
  464.       retval = IDecimalItem.valueOf(result);
  465.     }
  466.     return retval;
  467.   }

  468.   @NonNull
  469.   public static INumericItem opNumericUnaryMinus(@NonNull INumericItem item) {
  470.     INumericItem retval;
  471.     if (item instanceof IDecimalItem) {
  472.       // create a decimal result
  473.       BigDecimal decimal = item.asDecimal();

  474.       @SuppressWarnings("null")
  475.       @NonNull BigDecimal result = decimal.negate(FunctionUtils.MATH_CONTEXT);
  476.       retval = IDecimalItem.valueOf(result);
  477.     } else if (item instanceof IIntegerItem) {
  478.       // create a decimal result
  479.       BigInteger integer = item.asInteger();

  480.       @SuppressWarnings("null")
  481.       @NonNull BigInteger result = integer.negate();
  482.       retval = IIntegerItem.valueOf(result);
  483.     } else {
  484.       throw new InvalidTypeMetapathException(item);
  485.     }
  486.     return retval;
  487.   }

  488.   @NonNull
  489.   public static IBooleanItem opNumericEqual(@Nullable INumericItem arg1, @Nullable INumericItem arg2) {
  490.     IBooleanItem retval;
  491.     if (arg1 == null || arg2 == null) {
  492.       retval = IBooleanItem.FALSE;
  493.     } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) {
  494.       retval = IBooleanItem.valueOf(arg1.asDecimal().equals(arg2.asDecimal()));
  495.     } else {
  496.       retval = IBooleanItem.valueOf(arg1.asInteger().equals(arg2.asInteger()));
  497.     }
  498.     return retval;
  499.   }

  500.   @NonNull
  501.   public static IBooleanItem opNumericGreaterThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) {
  502.     IBooleanItem retval;
  503.     if (arg1 == null || arg2 == null) {
  504.       retval = IBooleanItem.FALSE;
  505.     } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) {
  506.       int result = arg1.asDecimal().compareTo(arg2.asDecimal());
  507.       retval = IBooleanItem.valueOf(result > 0);
  508.     } else {
  509.       int result = arg1.asInteger().compareTo(arg2.asInteger());
  510.       retval = IBooleanItem.valueOf(result > 0);
  511.     }
  512.     return retval;
  513.   }

  514.   @NonNull
  515.   public static IBooleanItem opNumericLessThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) {
  516.     IBooleanItem retval;
  517.     if (arg1 == null || arg2 == null) {
  518.       retval = IBooleanItem.FALSE;
  519.     } else if (arg1 instanceof IDecimalItem || arg2 instanceof IDecimalItem) {
  520.       int result = arg1.asDecimal().compareTo(arg2.asDecimal());
  521.       retval = IBooleanItem.valueOf(result < 0);
  522.     } else {
  523.       int result = arg1.asInteger().compareTo(arg2.asInteger());
  524.       retval = IBooleanItem.valueOf(result < 0);
  525.     }
  526.     return retval;
  527.   }

  528.   @NonNull
  529.   public static IBooleanItem opBooleanEqual(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) {
  530.     boolean left = arg1 != null && arg1.toBoolean();
  531.     boolean right = arg2 != null && arg2.toBoolean();

  532.     return IBooleanItem.valueOf(left == right);
  533.   }

  534.   @NonNull
  535.   public static IBooleanItem opBooleanGreaterThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) {
  536.     boolean left = arg1 != null && arg1.toBoolean();
  537.     boolean right = arg2 != null && arg2.toBoolean();

  538.     return IBooleanItem.valueOf(left && !right);
  539.   }

  540.   @NonNull
  541.   public static IBooleanItem opBooleanLessThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) {
  542.     boolean left = arg1 != null && arg1.toBoolean();
  543.     boolean right = arg2 != null && arg2.toBoolean();

  544.     return IBooleanItem.valueOf(!left && right);
  545.   }
  546. }