net.sf.saxon.expr.Calculator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr;
import net.sf.saxon.expr.parser.Token;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.transpile.CSharpReplaceBody;
import net.sf.saxon.type.AtomicType;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.Converter;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.*;
import net.sf.saxon.z.IntHashMap;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
/**
* 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;
/**
* Get the token number corresponding to the operator number
* @param operator the Calculator operator code
* @return the corresponding token
*/
public static int getTokenFromOperator(int operator) {
return tokens[operator];
}
private static final int[] tokens = new int[] {Token.PLUS, Token.MINUS, Token.MULT, Token.DIV, Token.MOD, Token.IDIV};
/**
* Get a short code to identify the calculator in expression export files
* @return a short identifying code that can be used to reconstruct the calculator
*/
public String code() {
String name = getClass().getSimpleName();
return name
.replace("Any", "a")
.replace("Double", "d")
.replace("Float", "f")
.replace("Decimal", "c")
.replace("Integer", "i")
.replace("Numeric", "n")
.replace("DateTime", "t")
.replace("Duration", "u")
.replace("Plus", "+")
.replace("Minus", "-")
.replace("Times", "*")
.replace("Div", "/")
.replace("Idiv", "~")
.replace("Mod", "%");
}
/**
* 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()
};
/**
* Marker interface for operations on doubles
*/
public interface DoubleOpDouble {};
public final static Calculator[] DOUBLE_FLOAT = DOUBLE_DOUBLE;
public final static Calculator[] DOUBLE_DECIMAL = DOUBLE_DOUBLE;
public final static Calculator[] DOUBLE_INTEGER = 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;
//public final static Calculator[] FLOAT_PDECIMAL = FLOAT_FLOAT;
public final static Calculator[] FLOAT_INTEGER = 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()
};
public final static Calculator[] DECIMAL_INTEGER = DECIMAL_DECIMAL;
/**
* Calculators used when the first operand is an integer
*/
public final static Calculator[] INTEGER_DOUBLE = DOUBLE_DOUBLE;
public final static Calculator[] INTEGER_FLOAT = FLOAT_FLOAT;
public final static Calculator[] INTEGER_DECIMAL = DECIMAL_DECIMAL;
public final static Calculator[] INTEGER_INTEGER = {
new IntegerPlusInteger(),
new IntegerMinusInteger(),
new IntegerTimesInteger(),
new IntegerDivInteger(),
new IntegerModInteger(),
new IntegerIdivInteger()
};
/**
* Calculators used when both operands are xs:dateTime, xs:date, or xs:time
*/
/*@Nullable*/ 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 final IntHashMap table = new IntHashMap<>(100);
private static final IntHashMap nameTable = new IntHashMap<>(100);
private static void def(int typeA, int typeB, Calculator[] calculatorSet, String setName) {
int key = (typeA & 0xffff) << 16 | (typeB & 0xffff);
table.put(key, calculatorSet);
nameTable.put(key, setName);
// 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, setName);
def(StandardNames.XS_YEAR_MONTH_DURATION, typeB, calculatorSet, setName);
}
if (typeB == StandardNames.XS_DURATION) {
def(typeA, StandardNames.XS_DAY_TIME_DURATION, calculatorSet, setName);
def(typeA, StandardNames.XS_YEAR_MONTH_DURATION, calculatorSet, setName);
}
if (typeA == StandardNames.XS_DATE_TIME) {
def(StandardNames.XS_DATE, typeB, calculatorSet, setName);
def(StandardNames.XS_TIME, typeB, calculatorSet, setName);
}
if (typeB == StandardNames.XS_DATE_TIME) {
def(typeA, StandardNames.XS_DATE, calculatorSet, setName);
def(typeA, StandardNames.XS_TIME, calculatorSet, setName);
}
if (typeA == StandardNames.XS_DOUBLE) {
def(StandardNames.XS_UNTYPED_ATOMIC, typeB, calculatorSet, setName);
}
if (typeB == StandardNames.XS_DOUBLE) {
def(typeA, StandardNames.XS_UNTYPED_ATOMIC, calculatorSet, setName);
}
}
/**
* 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) {
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];
}
}
/**
* Get a calculator given the short code used in the exported expression tree
* @param code the short code, e.g. i+i for IntegerPlusInteger
* @return the appropriate Calculator
*/
public static Calculator reconstructCalculator(String code) {
int typeA = typeFromCode(code.charAt(0));
int typeB = typeFromCode(code.charAt(2));
int operator = operatorFromCode(code.charAt(1));
return getCalculator(typeA, typeB, operator, false);
}
private static int typeFromCode(char code) {
switch (code) {
case 'a':
return StandardNames.XS_ANY_ATOMIC_TYPE;
case 'd':
return StandardNames.XS_DOUBLE;
case 'i':
return StandardNames.XS_INTEGER;
case 'f':
return StandardNames.XS_FLOAT;
case 'c':
return StandardNames.XS_DECIMAL;
case 'n':
return StandardNames.XS_NUMERIC;
case 't':
return StandardNames.XS_DATE_TIME;
case 'u':
return StandardNames.XS_DURATION;
default:
throw new AssertionError();
}
}
public static int operatorFromCode(char code) {
switch (code) {
case '+':
return PLUS;
case '-':
return MINUS;
case '*':
return TIMES;
case '/':
return DIV;
case '~':
return IDIV;
case '%':
return MOD;
default:
throw new AssertionError();
}
}
/**
* Get the name of the calculator set for a given combination of types
*
* @param typeA the fingerprint of the primitive type of the first operand
* @param typeB the fingerprint of the primitive type of the second operand
* @return null if no suitable Calculator can be found.
*/
public static String getCalculatorSetName(int typeA, int typeB) {
int key = (typeA & 0xffff) << 16 | (typeB & 0xffff);
return nameTable.get(key);
}
static {
def(StandardNames.XS_DOUBLE, StandardNames.XS_DOUBLE, DOUBLE_DOUBLE, "DOUBLE_DOUBLE");
def(StandardNames.XS_DOUBLE, StandardNames.XS_FLOAT, DOUBLE_FLOAT, "DOUBLE_FLOAT");
def(StandardNames.XS_DOUBLE, StandardNames.XS_DECIMAL, DOUBLE_DECIMAL, "DOUBLE_DECIMAL");
def(StandardNames.XS_DOUBLE, StandardNames.XS_INTEGER, DOUBLE_INTEGER, "DOUBLE_INTEGER");
def(StandardNames.XS_FLOAT, StandardNames.XS_DOUBLE, FLOAT_DOUBLE, "FLOAT_DOUBLE");
def(StandardNames.XS_FLOAT, StandardNames.XS_FLOAT, FLOAT_FLOAT, "FLOAT_FLOAT");
def(StandardNames.XS_FLOAT, StandardNames.XS_DECIMAL, FLOAT_DECIMAL, "FLOAT_DECIMAL");
def(StandardNames.XS_FLOAT, StandardNames.XS_INTEGER, FLOAT_INTEGER, "FLOAT_INTEGER");
def(StandardNames.XS_DECIMAL, StandardNames.XS_DOUBLE, DECIMAL_DOUBLE, "DECIMAL_DOUBLE");
def(StandardNames.XS_DECIMAL, StandardNames.XS_FLOAT, DECIMAL_FLOAT, "DECIMAL_FLOAT");
def(StandardNames.XS_DECIMAL, StandardNames.XS_DECIMAL, DECIMAL_DECIMAL, "DECIMAL_DECIMAL");
def(StandardNames.XS_DECIMAL, StandardNames.XS_INTEGER, DECIMAL_INTEGER, "DECIMAL_INTEGER");
def(StandardNames.XS_INTEGER, StandardNames.XS_DOUBLE, INTEGER_DOUBLE, "INTEGER_DOUBLE");
def(StandardNames.XS_INTEGER, StandardNames.XS_FLOAT, INTEGER_FLOAT, "INTEGER_FLOAT");
def(StandardNames.XS_INTEGER, StandardNames.XS_DECIMAL, INTEGER_DECIMAL, "INTEGER_DECIMAL");
def(StandardNames.XS_INTEGER, StandardNames.XS_INTEGER, INTEGER_INTEGER, "INTEGER_INTEGER");
def(StandardNames.XS_DATE_TIME, StandardNames.XS_DATE_TIME, DATETIME_DATETIME, "DATETIME_DATETIME");
def(StandardNames.XS_DATE_TIME, StandardNames.XS_DURATION, DATETIME_DURATION, "DATETIME_DURATION");
def(StandardNames.XS_DURATION, StandardNames.XS_DATE_TIME, DURATION_DATETIME, "DURATION_DATETIME");
def(StandardNames.XS_DURATION, StandardNames.XS_DURATION, DURATION_DURATION, "DURATION_DURATION");
def(StandardNames.XS_DURATION, StandardNames.XS_DOUBLE, DURATION_NUMERIC, "DURATION_NUMERIC");
def(StandardNames.XS_DURATION, StandardNames.XS_FLOAT, DURATION_NUMERIC, "DURATION_NUMERIC");
def(StandardNames.XS_DURATION, StandardNames.XS_DECIMAL, DURATION_NUMERIC, "DURATION_NUMERIC");
def(StandardNames.XS_DURATION, StandardNames.XS_INTEGER, DURATION_NUMERIC, "DURATION_NUMERIC");
def(StandardNames.XS_DOUBLE, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
def(StandardNames.XS_FLOAT, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
def(StandardNames.XS_DECIMAL, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
def(StandardNames.XS_INTEGER, StandardNames.XS_DURATION, NUMERIC_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
* @return the result of the computation, as a value of the correct primitive type
* @throws XPathException in the event of an arithmetic error
*/
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
*/
public static class AnyPlusAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: anyAtomicType - AnyAtomicType
*/
public static class AnyMinusAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: anyAtomicType * AnyAtomicType
*/
public static class AnyTimesAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: anyAtomicType div AnyAtomicType
*/
public static class AnyDivAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: anyAtomicType mod AnyAtomicType
*/
public static class AnyModAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: anyAtomicType idiv AnyAtomicType
*/
public static class AnyIdivAny extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
Calculator calc = getCalculator(
a.getItemType().getPrimitiveType(), b.getItemType().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);
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.ANY_ATOMIC;
}
}
/**
* Arithmetic: double + double (including types that promote to double)
*/
public static class DoublePlusDouble extends Calculator implements DoubleOpDouble {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new DoubleValue(((NumericValue) a).getDoubleValue() + ((NumericValue) b).getDoubleValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DOUBLE;
}
}
/**
* Arithmetic: double - double (including types that promote to double)
*/
public static class DoubleMinusDouble extends Calculator implements DoubleOpDouble{
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new DoubleValue(((NumericValue) a).getDoubleValue() - ((NumericValue) b).getDoubleValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DOUBLE;
}
}
/**
* Arithmetic: double * double (including types that promote to double)
*/
public static class DoubleTimesDouble extends Calculator implements DoubleOpDouble {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new DoubleValue(((NumericValue) a).getDoubleValue() * ((NumericValue) b).getDoubleValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DOUBLE;
}
}
/**
* Arithmetic: double div double (including types that promote to double)
*/
public static class DoubleDivDouble extends Calculator implements DoubleOpDouble {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new DoubleValue(((NumericValue) a).getDoubleValue() / ((NumericValue) b).getDoubleValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DOUBLE;
}
}
/**
* Arithmetic: double mod double (including types that promote to double)
*/
public static class DoubleModDouble extends Calculator implements DoubleOpDouble {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new DoubleValue(((NumericValue) a).getDoubleValue() % ((NumericValue) b).getDoubleValue());
}
@Override
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 implements DoubleOpDouble {
@Override
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 IntegerValue.fromDouble(A / B).asAtomic();
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: float + float (including types that promote to float)
*/
public static class FloatPlusFloat extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new FloatValue(((NumericValue) a).getFloatValue() + ((NumericValue) b).getFloatValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.FLOAT;
}
}
/**
* Arithmetic: float - float (including types that promote to float)
*/
public static class FloatMinusFloat extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new FloatValue(((NumericValue) a).getFloatValue() - ((NumericValue) b).getFloatValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.FLOAT;
}
}
/**
* Arithmetic: float * float (including types that promote to float)
*/
public static class FloatTimesFloat extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new FloatValue(((NumericValue) a).getFloatValue() * ((NumericValue) b).getFloatValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.FLOAT;
}
}
/**
* Arithmetic: float div float (including types that promote to float)
*/
public static class FloatDivFloat extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new FloatValue(((NumericValue) a).getFloatValue() / ((NumericValue) b).getFloatValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.FLOAT;
}
}
/**
* Arithmetic: float mod float (including types that promote to float)
*/
public static class FloatModFloat extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return new FloatValue(((NumericValue) a).getFloatValue() % ((NumericValue) b).getFloatValue());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.FLOAT;
}
}
/**
* Arithmetic: float idiv float (including types that promote to float)
*/
public static class FloatIdivFloat extends Calculator {
@Override
public IntegerValue 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 (Float.isNaN(A) || Float.isInfinite(A)) {
throw new XPathException("First operand of idiv is NaN or infinity", "FOAR0002", c);
}
if (Float.isNaN(B)) {
throw new XPathException("Second operand of idiv is NaN", "FOAR0002", c);
}
float quotient = A / B;
if (Float.isInfinite(quotient)) {
// bug 29171, test cbcl-numeric-idivide-008
return new DecimalIdivDecimal().compute(
new BigDecimalValue(((NumericValue) a).getDecimalValue()),
new BigDecimalValue(((NumericValue) b).getDecimalValue()),
c);
} else {
return (IntegerValue)Converter.FloatToInteger.INSTANCE.convert(new FloatValue(quotient)).asAtomic();
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: decimal + decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalPlusDecimal extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof IntegerValue && b instanceof IntegerValue) {
return ((IntegerValue) a).plus((IntegerValue) b);
} else {
return new BigDecimalValue(
((NumericValue) a).getDecimalValue().add(((NumericValue) b).getDecimalValue()));
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: decimal - decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalMinusDecimal extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof IntegerValue && b instanceof IntegerValue) {
return ((IntegerValue) a).minus((IntegerValue) b);
} else {
return new BigDecimalValue(
((NumericValue) a).getDecimalValue().subtract(((NumericValue) b).getDecimalValue()));
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: decimal * decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalTimesDecimal extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof IntegerValue && b instanceof IntegerValue) {
return ((IntegerValue) a).times((IntegerValue) b);
} else {
return new BigDecimalValue(
((NumericValue) a).getDecimalValue().multiply(((NumericValue) b).getDecimalValue()));
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: decimal div decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalDivDecimal extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return decimalDivide((NumericValue) a, (NumericValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
public static BigDecimalValue decimalDivide(NumericValue a, NumericValue b) throws XPathException {
if (b.signum() == 0) {
throw new XPathException("Decimal divide by zero", "FOAR0001");
}
final BigDecimal A = a.getDecimalValue();
final BigDecimal B = b.getDecimalValue();
BigDecimal result = internalDecimalDivide(A, B);
return new BigDecimalValue(result);
}
@CSharpReplaceBody(code="return Singulink.Numerics.BigDecimal.Divide(A, B, 18, Singulink.Numerics.RoundingMode.MidpointToZero);")
private static BigDecimal internalDecimalDivide(BigDecimal A, BigDecimal B) {
int scale = Math.max(BigDecimalValue.DIVIDE_PRECISION, A.scale() - B.scale() + BigDecimalValue.DIVIDE_PRECISION);
return A.divide(B, scale, RoundingMode.HALF_DOWN);
}
/**
* Arithmetic: decimal mod decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalModDecimal extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof IntegerValue && b instanceof IntegerValue) {
return ((IntegerValue) a).mod((IntegerValue) b);
}
if (((NumericValue)b).signum() == 0) {
throw new XPathException("Decimal modulo zero", "FOAR0001", c);
}
final BigDecimal A = ((NumericValue) a).getDecimalValue();
final BigDecimal B = ((NumericValue) b).getDecimalValue();
return new BigDecimalValue(A.remainder(B));
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: decimal idiv decimal (including types that promote to decimal, that is, integer)
*/
public static class DecimalIdivDecimal extends Calculator {
@Override
public IntegerValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof IntegerValue && b instanceof IntegerValue) {
return ((IntegerValue) a).idiv((IntegerValue) b);
}
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);
}
BigInteger quot = A.divideToIntegralValue(B).toBigInteger();
return BigIntegerValue.makeIntegerValue(quot);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: integer + integer
*/
public static class IntegerPlusInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).plus((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: integer - integer
*/
public static class IntegerMinusInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).minus((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: integer * integer
*/
public static class IntegerTimesInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).times((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: integer div integer
*/
public static class IntegerDivInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).div((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: integer mod integer
*/
public static class IntegerModInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).mod((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: integer idiv integer
*/
public static class IntegerIdivInteger extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((IntegerValue) a).idiv((IntegerValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.INTEGER;
}
}
/**
* Arithmetic: date/time/dateTime - date/time/dateTime
*/
private static class DateTimeMinusDateTime extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((CalendarValue) a).subtract((CalendarValue) b, c);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DAY_TIME_DURATION;
}
}
/**
* Arithmetic: date/time/dateTime + duration
*/
private static class DateTimePlusDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((CalendarValue) a).add((DurationValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
/**
* Arithmetic: date/time/dateTime - duration
*/
private static class DateTimeMinusDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((CalendarValue) a).add(((DurationValue) b).negate());
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
/**
* Arithmetic: duration + date/time/dateTime
*/
private static class DurationPlusDateTime extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((CalendarValue) b).add((DurationValue) a);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeB;
}
}
/**
* Arithmetic: duration + duration
*/
private static class DurationPlusDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((DurationValue) a).add((DurationValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
/**
* Arithmetic: duration - duration
*/
private static class DurationMinusDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((DurationValue) a).subtract((DurationValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
/**
* Arithmetic: duration div duration
*/
private static class DurationDivDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
return ((DurationValue) a).divide((DurationValue) b);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return BuiltInAtomicType.DECIMAL;
}
}
/**
* Arithmetic: duration * number
*/
private static class DurationTimesNumeric extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (b instanceof Int64Value) {
return ((DurationValue) a).multiply(((Int64Value) b).longValue());
} else if (b instanceof DecimalValue) {
return ((DurationValue) a).multiply(((DecimalValue) b).getDecimalValue());
} else {
return ((DurationValue) a).multiply(((NumericValue) b).getDoubleValue());
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
/**
* Arithmetic: number * duration
*/
private static class NumericTimesDuration extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
if (a instanceof Int64Value) {
return ((DurationValue) b).multiply(((Int64Value) a).longValue());
} else {
return ((DurationValue) b).multiply(((NumericValue) a).getDoubleValue());
}
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeB;
}
}
/**
* Arithmetic: duration div number
*/
private static class DurationDivNumeric extends Calculator {
@Override
public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
double d = 1.0 / ((NumericValue) b).getDoubleValue();
return ((DurationValue) a).multiply(d);
}
@Override
public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
return typeA;
}
}
}