package client.net.sf.saxon.ce.expr; import client.net.sf.saxon.ce.om.StandardNames; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.type.AtomicType; import client.net.sf.saxon.ce.type.BuiltInAtomicType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.type.TypeHierarchy; import client.net.sf.saxon.ce.value.*; import java.math.BigDecimal; import java.util.HashMap; /** * This class evaluates arithmetic expressions; it acts as a helper class to the ArithmeticExpression * class. There are many subclasses for the different kinds of arithmetic expression, and static methods * that allow the right subclass to be selected, either at compile time or at run time. */ public abstract class Calculator { public static final int PLUS = 0; public static final int MINUS = 1; public static final int TIMES = 2; public static final int DIV = 3; public static final int MOD = 4; public static final int IDIV = 5; /** * Calculators used for the six operators when the static type information does not allow * a more specific calculator to be chosen */ public final static Calculator[] ANY_ANY = { new AnyPlusAny(), new AnyMinusAny(), new AnyTimesAny(), new AnyDivAny(), new AnyModAny(), new AnyIdivAny() }; // NOTE: these fields are public because they are referenced from the Java code generated by the XQuery compiler /** * Calculators used when the first operand is a double */ public final static Calculator[] DOUBLE_DOUBLE = { new DoublePlusDouble(), new DoubleMinusDouble(), new DoubleTimesDouble(), new DoubleDivDouble(), new DoubleModDouble(), new DoubleIdivDouble() }; public final static Calculator[] DOUBLE_FLOAT = DOUBLE_DOUBLE; public final static Calculator[] DOUBLE_DECIMAL = DOUBLE_DOUBLE; /** * Calculators used when the first operand is a float */ public final static Calculator[] FLOAT_DOUBLE = DOUBLE_DOUBLE; public final static Calculator[] FLOAT_FLOAT = { new FloatPlusFloat(), new FloatMinusFloat(), new FloatTimesFloat(), new FloatDivFloat(), new FloatModFloat(), new FloatIdivFloat() }; public final static Calculator[] FLOAT_DECIMAL = FLOAT_FLOAT; /** * Calculators used when the first operand is a decimal */ public final static Calculator[] DECIMAL_DOUBLE = DOUBLE_DOUBLE; public final static Calculator[] DECIMAL_FLOAT = FLOAT_FLOAT; public final static Calculator[] DECIMAL_DECIMAL = { new DecimalPlusDecimal(), new DecimalMinusDecimal(), new DecimalTimesDecimal(), new DecimalDivDecimal(), new DecimalModDecimal(), new DecimalIdivDecimal() }; /** * Calculators used when both operands are xs:dateTime, xs:date, or xs:time */ public final static Calculator[] DATETIME_DATETIME = { null, new DateTimeMinusDateTime(), null, null, null, null }; /** * Calculators used when the first operand is xs:dateTime, xs:date, or xs:time, * and the second is a duration */ public final static Calculator[] DATETIME_DURATION = { new DateTimePlusDuration(), new DateTimeMinusDuration(), null, null, null, null }; /** * Calculators used when the second operand is xs:dateTime, xs:date, or xs:time, * and the first is a duration */ public final static Calculator[] DURATION_DATETIME = { new DurationPlusDateTime(), null, null, null, null, null }; /** * Calculators used when the both operands are durations */ public final static Calculator[] DURATION_DURATION = { new DurationPlusDuration(), new DurationMinusDuration(), null, new DurationDivDuration(), null, null }; /** * Calculators used when the first operand is a duration and the second is numeric */ public final static Calculator[] DURATION_NUMERIC = { null, null, new DurationTimesNumeric(), new DurationDivNumeric(), null, null }; /** * Calculators used when the second operand is a duration and the first is numeric */ public final static Calculator[] NUMERIC_DURATION = { null, null, new NumericTimesDuration(), null, null, null }; /** * Table mapping argument types to the Calculator class used to implement them */ private static HashMap<Integer, Calculator[]> table = new HashMap(100); private static void def(int typeA, int typeB, Calculator[] calculatorSet) { int key = (typeA & 0xffff)<<16 | (typeB & 0xffff); table.put(key, calculatorSet); // As well as the entries added directly, we also add derived entries for other types // considered primitive if (typeA == StandardNames.XS_DURATION) { def(StandardNames.XS_DAY_TIME_DURATION, typeB, calculatorSet); def(StandardNames.XS_YEAR_MONTH_DURATION, typeB, calculatorSet); } if (typeB == StandardNames.XS_DURATION) { def(typeA, StandardNames.XS_DAY_TIME_DURATION, calculatorSet); def(typeA, StandardNames.XS_YEAR_MONTH_DURATION, calculatorSet); } if (typeA == StandardNames.XS_DATE_TIME) { def(StandardNames.XS_DATE, typeB, calculatorSet); def(StandardNames.XS_TIME, typeB, calculatorSet); } if (typeB == StandardNames.XS_DATE_TIME) { def(typeA, StandardNames.XS_DATE, calculatorSet); def(typeA, StandardNames.XS_TIME, calculatorSet); } if (typeA == StandardNames.XS_DOUBLE) { def(StandardNames.XS_UNTYPED_ATOMIC, typeB, calculatorSet); } if (typeB == StandardNames.XS_DOUBLE) { def(typeA, StandardNames.XS_UNTYPED_ATOMIC, calculatorSet); } } /** * Factory method to get a calculator for a given combination of types * @param typeA fingerprint of the primitive type of the first operand * @param typeB fingerprint of the primitive type of the second operand * @param operator the arithmetic operator in use * @param mustResolve indicates that a concrete Calculator is required (rather than * an ANY_ANY calculator which needs to be further resolved at run-time) * @return null if no suitable Calculator can be found. */ public static Calculator getCalculator(int typeA, int typeB, int operator, boolean mustResolve) { if (typeA == StandardNames.XS_INTEGER) { typeA = StandardNames.XS_DECIMAL; } if (typeB == StandardNames.XS_INTEGER) { typeB = StandardNames.XS_DECIMAL; } int key = (typeA & 0xffff)<<16 | (typeB & 0xffff); Calculator[] set = table.get(key); if (set == null) { if (mustResolve) { return null; } else { return ANY_ANY[operator]; } } else { return set[operator]; } } static { def(StandardNames.XS_DOUBLE, StandardNames.XS_DOUBLE, DOUBLE_DOUBLE); def(StandardNames.XS_DOUBLE, StandardNames.XS_FLOAT, DOUBLE_FLOAT); def(StandardNames.XS_DOUBLE, StandardNames.XS_DECIMAL, DOUBLE_DECIMAL); def(StandardNames.XS_FLOAT, StandardNames.XS_DOUBLE, FLOAT_DOUBLE); def(StandardNames.XS_FLOAT, StandardNames.XS_FLOAT, FLOAT_FLOAT); def(StandardNames.XS_FLOAT, StandardNames.XS_DECIMAL, FLOAT_DECIMAL); def(StandardNames.XS_DECIMAL, StandardNames.XS_DOUBLE, DECIMAL_DOUBLE); def(StandardNames.XS_DECIMAL, StandardNames.XS_FLOAT, DECIMAL_FLOAT); def(StandardNames.XS_DECIMAL, StandardNames.XS_DECIMAL, DECIMAL_DECIMAL); def(StandardNames.XS_DATE_TIME, StandardNames.XS_DATE_TIME, DATETIME_DATETIME); def(StandardNames.XS_DATE_TIME, StandardNames.XS_DURATION, DATETIME_DURATION); def(StandardNames.XS_DURATION, StandardNames.XS_DATE_TIME, DURATION_DATETIME); def(StandardNames.XS_DURATION, StandardNames.XS_DURATION, DURATION_DURATION); def(StandardNames.XS_DURATION, StandardNames.XS_DOUBLE, DURATION_NUMERIC); def(StandardNames.XS_DURATION, StandardNames.XS_FLOAT, DURATION_NUMERIC); def(StandardNames.XS_DURATION, StandardNames.XS_DECIMAL, DURATION_NUMERIC); def(StandardNames.XS_DURATION, StandardNames.XS_INTEGER, DURATION_NUMERIC); def(StandardNames.XS_DOUBLE, StandardNames.XS_DURATION, NUMERIC_DURATION); def(StandardNames.XS_FLOAT, StandardNames.XS_DURATION, NUMERIC_DURATION); def(StandardNames.XS_DECIMAL, StandardNames.XS_DURATION, NUMERIC_DURATION); } /** * Perform an arithmetic operation * @param a the first operand. Must not be null, and must be an instance of the type implied by the * class name. * @param b the second operand. Must not be null, and must be an instance of the type implied by the * class name. * @param c the XPath dynamic evaluation context * @throws XPathException in the event of an arithmetic error * @return the result of the computation, as a value of the correct primitive type */ public abstract AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException; /** * Get the type of the result of the calculator, given arguments types typeA and typeB * @param typeA the type of the first operand * @param typeB the type of the second operand * @return the type of the result */ public abstract AtomicType getResultType(AtomicType typeA, AtomicType typeB); /** * Arithmetic: anyAtomicType + AnyAtomicType */ private static class AnyPlusAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), PLUS, true); if (calc == null) { throw new XPathException("Unsuitable types for + operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: anyAtomicType - AnyAtomicType */ private static class AnyMinusAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), MINUS, true); if (calc == null) { throw new XPathException("Unsuitable types for - operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: anyAtomicType * AnyAtomicType */ private static class AnyTimesAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), TIMES, true); if (calc == null) { throw new XPathException("Unsuitable types for * operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: anyAtomicType div AnyAtomicType */ private static class AnyDivAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), DIV, true); if (calc == null) { throw new XPathException("Unsuitable types for div operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: anyAtomicType mod AnyAtomicType */ private static class AnyModAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), MOD, true); if (calc == null) { throw new XPathException("Unsuitable types for mod operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: anyAtomicType idiv AnyAtomicType */ private static class AnyIdivAny extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { TypeHierarchy th = c.getConfiguration().getTypeHierarchy(); Calculator calc = getCalculator( a.getItemType(th).getPrimitiveType(), b.getItemType(th).getPrimitiveType(), IDIV, true); if (calc == null) { throw new XPathException("Unsuitable types for idiv operation (" + Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c); } else { return calc.compute(a, b, c); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.ANY_ATOMIC; } } /** * Arithmetic: double + double (including types that promote to double) */ private static class DoublePlusDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new DoubleValue(((NumericValue)a).getDoubleValue() + ((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DOUBLE; } } /** * Arithmetic: double - double (including types that promote to double) */ private static class DoubleMinusDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new DoubleValue(((NumericValue)a).getDoubleValue() - ((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DOUBLE; } } /** * Arithmetic: double * double (including types that promote to double) */ private static class DoubleTimesDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new DoubleValue(((NumericValue)a).getDoubleValue() * ((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DOUBLE; } } /** * Arithmetic: double div double (including types that promote to double) */ private static class DoubleDivDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new DoubleValue(((NumericValue)a).getDoubleValue() / ((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DOUBLE; } } /** * Arithmetic: double mod double (including types that promote to double) */ private static class DoubleModDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new DoubleValue(((NumericValue)a).getDoubleValue() % ((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DOUBLE; } } /** * Arithmetic: double idiv double (including types that promote to double) */ private static class DoubleIdivDouble extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { double A = ((NumericValue)a).getDoubleValue(); double B = ((NumericValue)b).getDoubleValue(); if (B == 0.0) { throw new XPathException("Integer division by zero", "FOAR0001", c); } if (Double.isNaN(A) || Double.isInfinite(A)) { throw new XPathException("First operand of idiv is NaN or infinity", "FOAR0002", c); } if (Double.isNaN(B)) { throw new XPathException("Second operand of idiv is NaN", "FOAR0002", c); } return new DoubleValue(A / B).convert(BuiltInAtomicType.INTEGER, true).asAtomic(); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.INTEGER; } } /** * Arithmetic: float + float (including types that promote to float) */ private static class FloatPlusFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new FloatValue(((NumericValue)a).getFloatValue() + ((NumericValue)b).getFloatValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.FLOAT; } } /** * Arithmetic: float - float (including types that promote to float) */ private static class FloatMinusFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new FloatValue(((NumericValue)a).getFloatValue() - ((NumericValue)b).getFloatValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.FLOAT; } } /** * Arithmetic: float * float (including types that promote to float) */ private static class FloatTimesFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new FloatValue(((NumericValue)a).getFloatValue() * ((NumericValue)b).getFloatValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.FLOAT; } } /** * Arithmetic: float div float (including types that promote to float) */ private static class FloatDivFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new FloatValue(((NumericValue)a).getFloatValue() / ((NumericValue)b).getFloatValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.FLOAT; } } /** * Arithmetic: float mod float (including types that promote to float) */ private static class FloatModFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return new FloatValue(((NumericValue)a).getFloatValue() % ((NumericValue)b).getFloatValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.FLOAT; } } /** * Arithmetic: float idiv float (including types that promote to float) */ private static class FloatIdivFloat extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { float A = ((NumericValue)a).getFloatValue(); float B = ((NumericValue)b).getFloatValue(); if (B == 0.0) { throw new XPathException("Integer division by zero", "FOAR0001", c); } if (Double.isNaN(A) || Double.isInfinite(A)) { throw new XPathException("First operand of idiv is NaN or infinity", "FOAR0002", c); } if (Double.isNaN(B)) { throw new XPathException("Second operand of idiv is NaN", "FOAR0002", c); } return new FloatValue(A / B).convert( BuiltInAtomicType.INTEGER, true).asAtomic(); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.INTEGER; } } /** * Arithmetic: decimal + decimal (including types that promote to decimal, that is, integer) */ private static class DecimalPlusDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { BigDecimal d = ((NumericValue)a).getDecimalValue().add(((NumericValue)b).getDecimalValue()); if (a instanceof IntegerValue && b instanceof IntegerValue) { return new IntegerValue(d); } else { return new DecimalValue(d); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } /** * Arithmetic: decimal - decimal (including types that promote to decimal, that is, integer) */ private static class DecimalMinusDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { BigDecimal d = ((NumericValue)a).getDecimalValue().subtract(((NumericValue)b).getDecimalValue()); if (a instanceof IntegerValue && b instanceof IntegerValue) { return new IntegerValue(d); } else { return new DecimalValue(d); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } /** * Arithmetic: decimal * decimal (including types that promote to decimal, that is, integer) */ private static class DecimalTimesDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { BigDecimal d = ((NumericValue)a).getDecimalValue().multiply(((NumericValue)b).getDecimalValue()); if (a instanceof IntegerValue && b instanceof IntegerValue) { return new IntegerValue(d); } else { return new DecimalValue(d); } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } /** * Arithmetic: decimal div decimal (including types that promote to decimal, that is, integer) */ private static class DecimalDivDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return decimalDivide((NumericValue)a, (NumericValue)b); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } public static DecimalValue decimalDivide(NumericValue a, NumericValue b) throws XPathException { final BigDecimal A = a.getDecimalValue(); final BigDecimal B = b.getDecimalValue(); int scale = Math.max(DecimalValue.DIVIDE_PRECISION, Math.max(A.scale(), B.scale())); try { BigDecimal result = A.divide(B, scale, BigDecimal.ROUND_HALF_DOWN); return new DecimalValue(result); } catch (ArithmeticException err) { if (b.compareTo(0) == 0) { throw new XPathException("Decimal divide by zero", "FOAR0001"); } else { throw err; } } } /** * Arithmetic: decimal mod decimal (including types that promote to decimal, that is, integer) */ private static class DecimalModDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { if (a instanceof IntegerValue && b instanceof IntegerValue) { return ((IntegerValue)a).mod((IntegerValue)b); } final BigDecimal A = ((NumericValue)a).getDecimalValue(); final BigDecimal B = ((NumericValue)b).getDecimalValue(); try { return new DecimalValue(A.remainder(B)); } catch (ArithmeticException err) { if (((NumericValue)b).compareTo(0) == 0) { throw new XPathException("Decimal modulo zero", "FOAR0001", c); } else { throw err; } } } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } /** * Arithmetic: decimal idiv decimal (including types that promote to decimal, that is, integer) */ private static class DecimalIdivDecimal extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { final BigDecimal A = ((NumericValue)a).getDecimalValue(); final BigDecimal B = ((NumericValue)b).getDecimalValue(); if (B.signum() == 0) { throw new XPathException("Integer division by zero", "FOAR0001", c); } BigDecimal quot = A.divideToIntegralValue(B); return IntegerValue.decimalToInteger(quot).asAtomic(); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.INTEGER; } } /** * Arithmetic: date/time/dateTime - date/time/dateTime */ private static class DateTimeMinusDateTime extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((CalendarValue)a).subtract((CalendarValue)b, c); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DAY_TIME_DURATION; } } /** * Arithmetic: date/time/dateTime + duration */ private static class DateTimePlusDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((CalendarValue)a).add((DurationValue)b); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } /** * Arithmetic: date/time/dateTime - duration */ private static class DateTimeMinusDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((CalendarValue)a).add(((DurationValue)b).multiply(-1.0)); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } /** * Arithmetic: duration + date/time/dateTime */ private static class DurationPlusDateTime extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((CalendarValue)b).add((DurationValue)a); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeB; } } /** * Arithmetic: duration + duration */ private static class DurationPlusDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((DurationValue)a).add((DurationValue)b); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } /** * Arithmetic: duration - duration */ private static class DurationMinusDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((DurationValue)a).subtract((DurationValue)b); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } /** * Arithmetic: duration div duration */ private static class DurationDivDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((DurationValue)a).divide((DurationValue)b); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return BuiltInAtomicType.DECIMAL; } } /** * Arithmetic: duration * number */ private static class DurationTimesNumeric extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((DurationValue)a).multiply(((NumericValue)b).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } /** * Arithmetic: number * duration */ private static class NumericTimesDuration extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { return ((DurationValue)b).multiply(((NumericValue)a).getDoubleValue()); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeB; } } /** * Arithmetic: duration div number */ private static class DurationDivNumeric extends Calculator { public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException { double d = 1.0 / ((NumericValue)b).getDoubleValue(); return ((DurationValue)a).multiply(d); } public AtomicType getResultType(AtomicType typeA, AtomicType typeB) { return typeA; } } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.