You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/08 17:56:58 UTC
[08/17] incubator-freemarker git commit: Renamed XxxUtil classes to
XxxUtils, as this convention is more widespread nowadays.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
new file mode 100644
index 0000000..6ffe937
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
@@ -0,0 +1,1289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.util._ClassUtils;
+import org.apache.freemarker.core.util._NumberUtils;
+
+/**
+ * Everything related to coercion to ambiguous numerical types.
+ */
+class OverloadedNumberUtils {
+
+ // Can't be instantiated
+ private OverloadedNumberUtils() { }
+
+ /**
+ * The lower limit of conversion prices where there's a risk of significant mantissa loss.
+ * The value comes from misc/overloadedNumberRules/prices.ods and generator.ftl.
+ */
+ static final int BIG_MANTISSA_LOSS_PRICE = 4 * 10000;
+
+ /** The highest long that can be stored in double without precision loss: 2**53. */
+ private static final long MAX_DOUBLE_OR_LONG = 9007199254740992L;
+ /** The lowest long that can be stored in double without precision loss: -(2**53). */
+ private static final long MIN_DOUBLE_OR_LONG = -9007199254740992L;
+ private static final int MAX_DOUBLE_OR_LONG_LOG_2 = 53;
+
+ /** The highest long that can be stored in float without precision loss: 2**24. */
+ private static final int MAX_FLOAT_OR_INT = 16777216;
+ /** The lowest long that can be stored in float without precision loss: -(2**24). */
+ private static final int MIN_FLOAT_OR_INT = -16777216;
+ private static final int MAX_FLOAT_OR_INT_LOG_2 = 24;
+ /** Lowest number that we don't thread as possible integer 0. */
+ private static final double LOWEST_ABOVE_ZERO = 0.000001;
+ /** Highest number that we don't thread as possible integer 1. */
+ private static final double HIGHEST_BELOW_ONE = 0.999999;
+
+ /**
+ * Attaches the lowest alternative number type to the parameter number via {@link NumberWithFallbackType}, if
+ * that's useful according the possible target number types. This transformation is applied on the method call
+ * argument list before overloaded method selection.
+ *
+ * <p>Note that as of this writing, this method is only used when
+ * {@link DefaultObjectWrapper#getIncompatibleImprovements()} >= 2.3.21.
+ *
+ * <p>Why's this needed, how it works: Overloaded method selection only selects methods where the <em>type</em>
+ * (not the value!) of the argument is "smaller" or the same as the parameter type. This is similar to how it's in
+ * the Java language. That it only decides based on the parameter type is important because this way
+ * {@link OverloadedMethodsSubset} can cache method lookup decisions using the types as the cache key. Problem is,
+ * since you don't declare the exact numerical types in FTL, and FTL has only a single generic numeric type
+ * anyway, what Java type a {@link TemplateNumberModel} uses internally is often seen as a technical detail of which
+ * the template author can't always keep track of. So we investigate the <em>value</em> of the number too,
+ * then coerce it down without overflow to a type that will match the most overloaded methods. (This
+ * is especially important as FTL often stores numbers in {@link BigDecimal}-s, which will hardly ever match any
+ * method parameters.) We could simply return that number, like {@code Byte(0)} for an {@code Integer(0)},
+ * however, then we would lose the information about what the original type was. The original type is sometimes
+ * important, as in ambiguous situations the method where there's an exact type match should be selected (like,
+ * when someone wants to select an overload explicitly with {@code m(x?int)}). Also, if an overload wins where
+ * the parameter type at the position of the number is {@code Number} or {@code Object} (or {@code Comparable}
+ * etc.), it's expected that we pass in the original value (an {@code Integer} in this example), especially if that
+ * value is the return value of another Java method. That's why we use
+ * {@link NumberWithFallbackType} numerical classes like {@link IntegerOrByte}, which represents both the original
+ * type and the coerced type, all encoded into the class of the value, which is used as the overloaded method lookup
+ * cache key.
+ *
+ * <p>See also: <tt>src\main\misc\overloadedNumberRules\prices.ods</tt>.
+ *
+ * @param num the number to coerce
+ * @param typeFlags the type flags of the target parameter position; see {@link TypeFlags}
+ *
+ * @returns The original number or a {@link NumberWithFallbackType}, depending on the actual value and the types
+ * indicated in the {@code targetNumTypes} parameter.
+ */
+ static Number addFallbackType(final Number num, final int typeFlags) {
+ final Class numClass = num.getClass();
+ if (numClass == BigDecimal.class) {
+ // For now we only support the backward-compatible mode that doesn't prevent roll overs and magnitude loss.
+ // However, we push the overloaded selection to the right direction, so we will at least indicate if the
+ // number has decimals.
+ BigDecimal n = (BigDecimal) num;
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) != 0
+ && (typeFlags & TypeFlags.MASK_KNOWN_NONINTEGERS) != 0
+ && _NumberUtils.isIntegerBigDecimal(n) /* <- can be expensive */) {
+ return new IntegerBigDecimal(n);
+ } else {
+ // Either it was a non-integer, or it didn't mater what it was, as we don't have both integer and
+ // non-integer target types.
+ return n;
+ }
+ } else if (numClass == Integer.class) {
+ int pn = num.intValue();
+ // Note that we try to return the most specific type (i.e., the numerical type with the smallest range), but
+ // only among the types that are possible targets. Like if the only target is int and the value is 1, we
+ // will return Integer 1, not Byte 1, even though byte is automatically converted to int so it would
+ // work too. Why we avoid unnecessarily specific types is that they generate more overloaded method lookup
+ // cache entries, since the cache key is the array of the types of the argument values. So we want as few
+ // permutations as possible.
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new IntegerOrByte((Integer) num, (byte) pn);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
+ return new IntegerOrShort((Integer) num, (short) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == Long.class) {
+ final long pn = num.longValue();
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new LongOrByte((Long) num, (byte) pn);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
+ return new LongOrShort((Long) num, (short) pn);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0 && pn <= Integer.MAX_VALUE && pn >= Integer.MIN_VALUE) {
+ return new LongOrInteger((Long) num, (int) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == Double.class) {
+ final double doubleN = num.doubleValue();
+
+ // Can we store it in an integer type?
+ checkIfWholeNumber: do {
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
+
+ // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
+ if (doubleN > MAX_DOUBLE_OR_LONG || doubleN < MIN_DOUBLE_OR_LONG) break checkIfWholeNumber;
+
+ long longN = num.longValue();
+ double diff = doubleN - longN;
+ boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
+ if (diff == 0) {
+ exact = true;
+ } else if (diff > 0) {
+ if (diff < LOWEST_ABOVE_ZERO) {
+ exact = false;
+ } else if (diff > HIGHEST_BELOW_ONE) {
+ exact = false;
+ longN++;
+ } else {
+ break checkIfWholeNumber;
+ }
+ } else { // => diff < 0
+ if (diff > -LOWEST_ABOVE_ZERO) {
+ exact = false;
+ } else if (diff < -HIGHEST_BELOW_ONE) {
+ exact = false;
+ longN--;
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+
+ // If we reach this, it can be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.BYTE) != 0
+ && longN <= Byte.MAX_VALUE && longN >= Byte.MIN_VALUE) {
+ return new DoubleOrByte((Double) num, (byte) longN);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0
+ && longN <= Short.MAX_VALUE && longN >= Short.MIN_VALUE) {
+ return new DoubleOrShort((Double) num, (short) longN);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0
+ && longN <= Integer.MAX_VALUE && longN >= Integer.MIN_VALUE) {
+ final int intN = (int) longN;
+ return (typeFlags & TypeFlags.FLOAT) != 0 && intN >= MIN_FLOAT_OR_INT && intN <= MAX_FLOAT_OR_INT
+ ? new DoubleOrIntegerOrFloat((Double) num, intN)
+ : new DoubleOrInteger((Double) num, intN);
+ } else if ((typeFlags & TypeFlags.LONG) != 0) {
+ if (exact) {
+ return new DoubleOrLong((Double) num, longN);
+ } else {
+ // We don't deal with non-exact numbers outside the range of int, as we already reach
+ // ULP 2.384185791015625E-7 there.
+ if (longN >= Integer.MIN_VALUE && longN <= Integer.MAX_VALUE) {
+ return new DoubleOrLong((Double) num, longN);
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+ }
+ // This point is reached if the double value was out of the range of target integer type(s).
+ // Falls through!
+ } while (false);
+ // If we reach this that means that it can't be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.FLOAT) != 0 && doubleN >= -Float.MAX_VALUE && doubleN <= Float.MAX_VALUE) {
+ return new DoubleOrFloat((Double) num);
+ } else {
+ // Simply Double:
+ return num;
+ }
+ } else if (numClass == Float.class) {
+ final float floatN = num.floatValue();
+
+ // Can we store it in an integer type?
+ checkIfWholeNumber: do {
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
+
+ // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
+ if (floatN > MAX_FLOAT_OR_INT || floatN < MIN_FLOAT_OR_INT) break checkIfWholeNumber;
+
+ int intN = num.intValue();
+ double diff = floatN - intN;
+ boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
+ if (diff == 0) {
+ exact = true;
+ // We already reach ULP 7.6293945E-6 with bytes, so we don't continue with shorts.
+ } else if (intN >= Byte.MIN_VALUE && intN <= Byte.MAX_VALUE) {
+ if (diff > 0) {
+ if (diff < 0.00001) {
+ exact = false;
+ } else if (diff > 0.99999) {
+ exact = false;
+ intN++;
+ } else {
+ break checkIfWholeNumber;
+ }
+ } else { // => diff < 0
+ if (diff > -0.00001) {
+ exact = false;
+ } else if (diff < -0.99999) {
+ exact = false;
+ intN--;
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+ } else {
+ break checkIfWholeNumber;
+ }
+
+ // If we reach this, it can be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.BYTE) != 0 && intN <= Byte.MAX_VALUE && intN >= Byte.MIN_VALUE) {
+ return new FloatOrByte((Float) num, (byte) intN);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && intN <= Short.MAX_VALUE && intN >= Short.MIN_VALUE) {
+ return new FloatOrShort((Float) num, (short) intN);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0) {
+ return new FloatOrInteger((Float) num, intN);
+ } else if ((typeFlags & TypeFlags.LONG) != 0) {
+ // We can't even go outside the range of integers, so we don't need Long variation:
+ return exact
+ ? new FloatOrInteger((Float) num, intN)
+ : new FloatOrByte((Float) num, (byte) intN); // as !exact implies (-128..127)
+ }
+ // This point is reached if the float value was out of the range of target integer type(s).
+ // Falls through!
+ } while (false);
+ // If we reach this that means that it can't be treated as a whole number. So it's simply a Float:
+ return num;
+ } else if (numClass == Byte.class) {
+ return num;
+ } else if (numClass == Short.class) {
+ short pn = num.shortValue();
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new ShortOrByte((Short) num, (byte) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == BigInteger.class) {
+ if ((typeFlags
+ & ((TypeFlags.MASK_KNOWN_INTEGERS | TypeFlags.MASK_KNOWN_NONINTEGERS)
+ ^ (TypeFlags.BIG_INTEGER | TypeFlags.BIG_DECIMAL))) != 0) {
+ BigInteger biNum = (BigInteger) num;
+ final int bitLength = biNum.bitLength(); // Doesn't include sign bit, so it's one less than expected
+ if ((typeFlags & TypeFlags.BYTE) != 0 && bitLength <= 7) {
+ return new BigIntegerOrByte(biNum);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && bitLength <= 15) {
+ return new BigIntegerOrShort(biNum);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0 && bitLength <= 31) {
+ return new BigIntegerOrInteger(biNum);
+ } else if ((typeFlags & TypeFlags.LONG) != 0 && bitLength <= 63) {
+ return new BigIntegerOrLong(biNum);
+ } else if ((typeFlags & TypeFlags.FLOAT) != 0
+ && (bitLength <= MAX_FLOAT_OR_INT_LOG_2
+ || bitLength == MAX_FLOAT_OR_INT_LOG_2 + 1
+ && biNum.getLowestSetBit() >= MAX_FLOAT_OR_INT_LOG_2)) {
+ return new BigIntegerOrFloat(biNum);
+ } else if ((typeFlags & TypeFlags.DOUBLE) != 0
+ && (bitLength <= MAX_DOUBLE_OR_LONG_LOG_2
+ || bitLength == MAX_DOUBLE_OR_LONG_LOG_2 + 1
+ && biNum.getLowestSetBit() >= MAX_DOUBLE_OR_LONG_LOG_2)) {
+ return new BigIntegerOrDouble(biNum);
+ } else {
+ return num;
+ }
+ } else {
+ // No relevant coercion target types; return the BigInteger as is:
+ return num;
+ }
+ } else {
+ // Unknown number type:
+ return num;
+ }
+ }
+
+ interface ByteSource { Byte byteValue(); }
+ interface ShortSource { Short shortValue(); }
+ interface IntegerSource { Integer integerValue(); }
+ interface LongSource { Long longValue(); }
+ interface FloatSource { Float floatValue(); }
+ interface DoubleSource { Double doubleValue(); }
+ interface BigIntegerSource { BigInteger bigIntegerValue(); }
+ interface BigDecimalSource { BigDecimal bigDecimalValue(); }
+
+ /**
+ * Superclass of "Or"-ed numerical types. With an example, a {@code int} 1 has the fallback type {@code byte}, as
+ * that's the smallest type that can store the value, so it can be represented as an {@link IntegerOrByte}.
+ * This is useful as overloaded method selection only examines the type of the arguments, not the value of them,
+ * but with "Or"-ed types we can encode this value-related information into the argument type, hence influencing the
+ * method selection.
+ */
+ abstract static class NumberWithFallbackType extends Number implements Comparable {
+
+ protected abstract Number getSourceNumber();
+
+ @Override
+ public int intValue() {
+ return getSourceNumber().intValue();
+ }
+
+ @Override
+ public long longValue() {
+ return getSourceNumber().longValue();
+ }
+
+ @Override
+ public float floatValue() {
+ return getSourceNumber().floatValue();
+ }
+
+ @Override
+ public double doubleValue() {
+ return getSourceNumber().doubleValue();
+ }
+
+ @Override
+ public byte byteValue() {
+ return getSourceNumber().byteValue();
+ }
+
+ @Override
+ public short shortValue() {
+ return getSourceNumber().shortValue();
+ }
+
+ @Override
+ public int hashCode() {
+ return getSourceNumber().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj != null && getClass() == obj.getClass()) {
+ return getSourceNumber().equals(((NumberWithFallbackType) obj).getSourceNumber());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getSourceNumber().toString();
+ }
+
+ // We have to implement this, so that if a potential matching method expects a Comparable, which is implemented
+ // by all the supported numerical types, the "Or" type will be a match.
+ @Override
+ public int compareTo(Object o) {
+ Number n = getSourceNumber();
+ if (n instanceof Comparable) {
+ return ((Comparable) n).compareTo(o);
+ } else {
+ throw new ClassCastException(n.getClass().getName() + " is not Comparable.");
+ }
+ }
+
+ }
+
+ /**
+ * Holds a {@link BigDecimal} that stores a whole number. When selecting a overloaded method, FreeMarker tries to
+ * associate {@link BigDecimal} values to parameters of types that can hold non-whole numbers, unless the
+ * {@link BigDecimal} is wrapped into this class, in which case it does the opposite. This mechanism is, however,
+ * too rough to prevent roll overs or magnitude losses. Those are not yet handled for backward compatibility (they
+ * were suppressed earlier too).
+ */
+ static final class IntegerBigDecimal extends NumberWithFallbackType {
+
+ private final BigDecimal n;
+
+ IntegerBigDecimal(BigDecimal n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ public BigInteger bigIntegerValue() {
+ return n.toBigInteger();
+ }
+
+ }
+
+ static abstract class LongOrSmallerInteger extends NumberWithFallbackType {
+
+ private final Long n;
+
+ protected LongOrSmallerInteger(Long n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public long longValue() {
+ return n.longValue();
+ }
+
+ }
+
+ static class LongOrByte extends LongOrSmallerInteger {
+
+ private final byte w;
+
+ LongOrByte(Long n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static class LongOrShort extends LongOrSmallerInteger {
+
+ private final short w;
+
+ LongOrShort(Long n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ }
+
+ static class LongOrInteger extends LongOrSmallerInteger {
+
+ private final int w;
+
+ LongOrInteger(Long n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ }
+
+ static abstract class IntegerOrSmallerInteger extends NumberWithFallbackType {
+
+ private final Integer n;
+
+ protected IntegerOrSmallerInteger(Integer n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public int intValue() {
+ return n.intValue();
+ }
+
+ }
+
+ static class IntegerOrByte extends IntegerOrSmallerInteger {
+
+ private final byte w;
+
+ IntegerOrByte(Integer n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static class IntegerOrShort extends IntegerOrSmallerInteger {
+
+ private final short w;
+
+ IntegerOrShort(Integer n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ }
+
+ static class ShortOrByte extends NumberWithFallbackType {
+
+ private final Short n;
+ private final byte w;
+
+ protected ShortOrByte(Short n, byte w) {
+ this.n = n;
+ this.w = w;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public short shortValue() {
+ return n.shortValue();
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static abstract class DoubleOrWholeNumber extends NumberWithFallbackType {
+
+ private final Double n;
+
+ protected DoubleOrWholeNumber(Double n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public double doubleValue() {
+ return n.doubleValue();
+ }
+
+ }
+
+ static final class DoubleOrByte extends DoubleOrWholeNumber {
+
+ private final byte w;
+
+ DoubleOrByte(Double n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrShort extends DoubleOrWholeNumber {
+
+ private final short w;
+
+ DoubleOrShort(Double n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrIntegerOrFloat extends DoubleOrWholeNumber {
+
+ private final int w;
+
+ DoubleOrIntegerOrFloat(Double n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrInteger extends DoubleOrWholeNumber {
+
+ private final int w;
+
+ DoubleOrInteger(Double n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrLong extends DoubleOrWholeNumber {
+
+ private final long w;
+
+ DoubleOrLong(Double n, long w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrFloat extends NumberWithFallbackType {
+
+ private final Double n;
+
+ DoubleOrFloat(Double n) {
+ this.n = n;
+ }
+
+ @Override
+ public float floatValue() {
+ return n.floatValue();
+ }
+
+ @Override
+ public double doubleValue() {
+ return n.doubleValue();
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ }
+
+ static abstract class FloatOrWholeNumber extends NumberWithFallbackType {
+
+ private final Float n;
+
+ FloatOrWholeNumber(Float n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public float floatValue() {
+ return n.floatValue();
+ }
+
+ }
+
+ static final class FloatOrByte extends FloatOrWholeNumber {
+
+ private final byte w;
+
+ FloatOrByte(Float n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class FloatOrShort extends FloatOrWholeNumber {
+
+ private final short w;
+
+ FloatOrShort(Float n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class FloatOrInteger extends FloatOrWholeNumber {
+
+ private final int w;
+
+ FloatOrInteger(Float n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ abstract static class BigIntegerOrPrimitive extends NumberWithFallbackType {
+
+ protected final BigInteger n;
+
+ BigIntegerOrPrimitive(BigInteger n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ }
+
+ final static class BigIntegerOrByte extends BigIntegerOrPrimitive {
+
+ BigIntegerOrByte(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrShort extends BigIntegerOrPrimitive {
+
+ BigIntegerOrShort(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrInteger extends BigIntegerOrPrimitive {
+
+ BigIntegerOrInteger(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrLong extends BigIntegerOrPrimitive {
+
+ BigIntegerOrLong(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ abstract static class BigIntegerOrFPPrimitive extends BigIntegerOrPrimitive {
+
+ BigIntegerOrFPPrimitive(BigInteger n) {
+ super(n);
+ }
+
+ /** Faster version of {@link BigDecimal#floatValue()}, utilizes that the number known to fit into a long. */
+ @Override
+ public float floatValue() {
+ return n.longValue();
+ }
+
+ /** Faster version of {@link BigDecimal#doubleValue()}, utilizes that the number known to fit into a long. */
+ @Override
+ public double doubleValue() {
+ return n.longValue();
+ }
+
+ }
+
+ final static class BigIntegerOrFloat extends BigIntegerOrFPPrimitive {
+
+ BigIntegerOrFloat(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrDouble extends BigIntegerOrFPPrimitive {
+
+ BigIntegerOrDouble(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ /**
+ * Returns a non-negative number that indicates how much we want to avoid a given numerical type conversion. Since
+ * we only consider the types here, not the actual value, we always consider the worst case scenario. Like it will
+ * say that converting int to short is not allowed, although int 1 can be converted to byte without loss. To account
+ * for such situations, "Or"-ed types, like {@link IntegerOrByte} has to be used.
+ *
+ * @param fromC the non-primitive type of the argument (with other words, the actual type).
+ * Must be {@link Number} or its subclass. This is possibly an {@link NumberWithFallbackType} subclass.
+ * @param toC the <em>non-primitive</em> type of the target parameter (with other words, the format type).
+ * Must be a {@link Number} subclass, not {@link Number} itself.
+ * Must <em>not</em> be {@link NumberWithFallbackType} or its subclass.
+ *
+ * @return
+ * <p>The possible values are:
+ * <ul>
+ * <li>0: No conversion is needed
+ * <li>[0, 30000): Lossless conversion
+ * <li>[30000, 40000): Smaller precision loss in mantissa is possible.
+ * <li>[40000, 50000): Bigger precision loss in mantissa is possible.
+ * <li>{@link Integer#MAX_VALUE}: Conversion not allowed due to the possibility of magnitude loss or
+ * overflow</li>
+ * </ul>
+ *
+ * <p>At some places, we only care if the conversion is possible, i.e., whether the return value is
+ * {@link Integer#MAX_VALUE} or not. But when multiple overloaded methods have an argument type to which we
+ * could convert to, this number will influence which of those will be chosen.
+ */
+ static int getArgumentConversionPrice(Class fromC, Class toC) {
+ // DO NOT EDIT, generated code!
+ // See: src\main\misc\overloadedNumberRules\README.txt
+ if (toC == fromC) {
+ return 0;
+ } else if (toC == Integer.class) {
+ if (fromC == IntegerBigDecimal.class) return 31003;
+ else if (fromC == BigDecimal.class) return 41003;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10003;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 21003;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 22003;
+ else if (fromC == DoubleOrInteger.class) return 22003;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 0;
+ else if (fromC == DoubleOrByte.class) return 22003;
+ else if (fromC == LongOrByte.class) return 21003;
+ else if (fromC == Short.class) return 10003;
+ else if (fromC == LongOrShort.class) return 21003;
+ else if (fromC == ShortOrByte.class) return 10003;
+ else if (fromC == FloatOrInteger.class) return 21003;
+ else if (fromC == FloatOrByte.class) return 21003;
+ else if (fromC == FloatOrShort.class) return 21003;
+ else if (fromC == BigIntegerOrInteger.class) return 16003;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 16003;
+ else if (fromC == IntegerOrShort.class) return 0;
+ else if (fromC == DoubleOrShort.class) return 22003;
+ else if (fromC == BigIntegerOrShort.class) return 16003;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Long.class) {
+ if (fromC == Integer.class) return 10004;
+ else if (fromC == IntegerBigDecimal.class) return 31004;
+ else if (fromC == BigDecimal.class) return 41004;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10004;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 0;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 21004;
+ else if (fromC == DoubleOrInteger.class) return 21004;
+ else if (fromC == DoubleOrLong.class) return 21004;
+ else if (fromC == IntegerOrByte.class) return 10004;
+ else if (fromC == DoubleOrByte.class) return 21004;
+ else if (fromC == LongOrByte.class) return 0;
+ else if (fromC == Short.class) return 10004;
+ else if (fromC == LongOrShort.class) return 0;
+ else if (fromC == ShortOrByte.class) return 10004;
+ else if (fromC == FloatOrInteger.class) return 21004;
+ else if (fromC == FloatOrByte.class) return 21004;
+ else if (fromC == FloatOrShort.class) return 21004;
+ else if (fromC == BigIntegerOrInteger.class) return 15004;
+ else if (fromC == BigIntegerOrLong.class) return 15004;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 15004;
+ else if (fromC == IntegerOrShort.class) return 10004;
+ else if (fromC == DoubleOrShort.class) return 21004;
+ else if (fromC == BigIntegerOrShort.class) return 15004;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Double.class) {
+ if (fromC == Integer.class) return 20007;
+ else if (fromC == IntegerBigDecimal.class) return 32007;
+ else if (fromC == BigDecimal.class) return 32007;
+ else if (fromC == Long.class) return 30007;
+ else if (fromC == Float.class) return 10007;
+ else if (fromC == Byte.class) return 20007;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 21007;
+ else if (fromC == DoubleOrFloat.class) return 0;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 0;
+ else if (fromC == DoubleOrInteger.class) return 0;
+ else if (fromC == DoubleOrLong.class) return 0;
+ else if (fromC == IntegerOrByte.class) return 20007;
+ else if (fromC == DoubleOrByte.class) return 0;
+ else if (fromC == LongOrByte.class) return 21007;
+ else if (fromC == Short.class) return 20007;
+ else if (fromC == LongOrShort.class) return 21007;
+ else if (fromC == ShortOrByte.class) return 20007;
+ else if (fromC == FloatOrInteger.class) return 10007;
+ else if (fromC == FloatOrByte.class) return 10007;
+ else if (fromC == FloatOrShort.class) return 10007;
+ else if (fromC == BigIntegerOrInteger.class) return 20007;
+ else if (fromC == BigIntegerOrLong.class) return 30007;
+ else if (fromC == BigIntegerOrDouble.class) return 20007;
+ else if (fromC == BigIntegerOrFloat.class) return 20007;
+ else if (fromC == BigIntegerOrByte.class) return 20007;
+ else if (fromC == IntegerOrShort.class) return 20007;
+ else if (fromC == DoubleOrShort.class) return 0;
+ else if (fromC == BigIntegerOrShort.class) return 20007;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Float.class) {
+ if (fromC == Integer.class) return 30006;
+ else if (fromC == IntegerBigDecimal.class) return 33006;
+ else if (fromC == BigDecimal.class) return 33006;
+ else if (fromC == Long.class) return 40006;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 20006;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 30006;
+ else if (fromC == DoubleOrFloat.class) return 30006;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 23006;
+ else if (fromC == DoubleOrInteger.class) return 30006;
+ else if (fromC == DoubleOrLong.class) return 40006;
+ else if (fromC == IntegerOrByte.class) return 24006;
+ else if (fromC == DoubleOrByte.class) return 23006;
+ else if (fromC == LongOrByte.class) return 24006;
+ else if (fromC == Short.class) return 20006;
+ else if (fromC == LongOrShort.class) return 24006;
+ else if (fromC == ShortOrByte.class) return 20006;
+ else if (fromC == FloatOrInteger.class) return 0;
+ else if (fromC == FloatOrByte.class) return 0;
+ else if (fromC == FloatOrShort.class) return 0;
+ else if (fromC == BigIntegerOrInteger.class) return 30006;
+ else if (fromC == BigIntegerOrLong.class) return 40006;
+ else if (fromC == BigIntegerOrDouble.class) return 40006;
+ else if (fromC == BigIntegerOrFloat.class) return 24006;
+ else if (fromC == BigIntegerOrByte.class) return 24006;
+ else if (fromC == IntegerOrShort.class) return 24006;
+ else if (fromC == DoubleOrShort.class) return 23006;
+ else if (fromC == BigIntegerOrShort.class) return 24006;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Byte.class) {
+ if (fromC == Integer.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerBigDecimal.class) return 35001;
+ else if (fromC == BigDecimal.class) return 45001;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 22001;
+ else if (fromC == DoubleOrByte.class) return 25001;
+ else if (fromC == LongOrByte.class) return 23001;
+ else if (fromC == Short.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == ShortOrByte.class) return 21001;
+ else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == FloatOrByte.class) return 23001;
+ else if (fromC == FloatOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 18001;
+ else if (fromC == IntegerOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrShort.class) return Integer.MAX_VALUE;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Short.class) {
+ if (fromC == Integer.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerBigDecimal.class) return 34002;
+ else if (fromC == BigDecimal.class) return 44002;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10002;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 21002;
+ else if (fromC == DoubleOrByte.class) return 24002;
+ else if (fromC == LongOrByte.class) return 22002;
+ else if (fromC == LongOrShort.class) return 22002;
+ else if (fromC == ShortOrByte.class) return 0;
+ else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == FloatOrByte.class) return 22002;
+ else if (fromC == FloatOrShort.class) return 22002;
+ else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 17002;
+ else if (fromC == IntegerOrShort.class) return 21002;
+ else if (fromC == DoubleOrShort.class) return 24002;
+ else if (fromC == BigIntegerOrShort.class) return 17002;
+ else return Integer.MAX_VALUE;
+ } else if (toC == BigDecimal.class) {
+ if (fromC == Integer.class) return 20008;
+ else if (fromC == IntegerBigDecimal.class) return 0;
+ else if (fromC == Long.class) return 20008;
+ else if (fromC == Double.class) return 20008;
+ else if (fromC == Float.class) return 20008;
+ else if (fromC == Byte.class) return 20008;
+ else if (fromC == BigInteger.class) return 10008;
+ else if (fromC == LongOrInteger.class) return 20008;
+ else if (fromC == DoubleOrFloat.class) return 20008;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 20008;
+ else if (fromC == DoubleOrInteger.class) return 20008;
+ else if (fromC == DoubleOrLong.class) return 20008;
+ else if (fromC == IntegerOrByte.class) return 20008;
+ else if (fromC == DoubleOrByte.class) return 20008;
+ else if (fromC == LongOrByte.class) return 20008;
+ else if (fromC == Short.class) return 20008;
+ else if (fromC == LongOrShort.class) return 20008;
+ else if (fromC == ShortOrByte.class) return 20008;
+ else if (fromC == FloatOrInteger.class) return 20008;
+ else if (fromC == FloatOrByte.class) return 20008;
+ else if (fromC == FloatOrShort.class) return 20008;
+ else if (fromC == BigIntegerOrInteger.class) return 10008;
+ else if (fromC == BigIntegerOrLong.class) return 10008;
+ else if (fromC == BigIntegerOrDouble.class) return 10008;
+ else if (fromC == BigIntegerOrFloat.class) return 10008;
+ else if (fromC == BigIntegerOrByte.class) return 10008;
+ else if (fromC == IntegerOrShort.class) return 20008;
+ else if (fromC == DoubleOrShort.class) return 20008;
+ else if (fromC == BigIntegerOrShort.class) return 10008;
+ else return Integer.MAX_VALUE;
+ } else if (toC == BigInteger.class) {
+ if (fromC == Integer.class) return 10005;
+ else if (fromC == IntegerBigDecimal.class) return 10005;
+ else if (fromC == BigDecimal.class) return 40005;
+ else if (fromC == Long.class) return 10005;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10005;
+ else if (fromC == LongOrInteger.class) return 10005;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 21005;
+ else if (fromC == DoubleOrInteger.class) return 21005;
+ else if (fromC == DoubleOrLong.class) return 21005;
+ else if (fromC == IntegerOrByte.class) return 10005;
+ else if (fromC == DoubleOrByte.class) return 21005;
+ else if (fromC == LongOrByte.class) return 10005;
+ else if (fromC == Short.class) return 10005;
+ else if (fromC == LongOrShort.class) return 10005;
+ else if (fromC == ShortOrByte.class) return 10005;
+ else if (fromC == FloatOrInteger.class) return 25005;
+ else if (fromC == FloatOrByte.class) return 25005;
+ else if (fromC == FloatOrShort.class) return 25005;
+ else if (fromC == BigIntegerOrInteger.class) return 0;
+ else if (fromC == BigIntegerOrLong.class) return 0;
+ else if (fromC == BigIntegerOrDouble.class) return 0;
+ else if (fromC == BigIntegerOrFloat.class) return 0;
+ else if (fromC == BigIntegerOrByte.class) return 0;
+ else if (fromC == IntegerOrShort.class) return 10005;
+ else if (fromC == DoubleOrShort.class) return 21005;
+ else if (fromC == BigIntegerOrShort.class) return 0;
+ else return Integer.MAX_VALUE;
+ } else {
+ // Unknown toC; we don't know how to convert to it:
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ static int compareNumberTypeSpecificity(Class c1, Class c2) {
+ // DO NOT EDIT, generated code!
+ // See: src\main\misc\overloadedNumberRules\README.txt
+ c1 = _ClassUtils.primitiveClassToBoxingClass(c1);
+ c2 = _ClassUtils.primitiveClassToBoxingClass(c2);
+
+ if (c1 == c2) return 0;
+
+ if (c1 == Integer.class) {
+ if (c2 == Long.class) return 4 - 3;
+ if (c2 == Double.class) return 7 - 3;
+ if (c2 == Float.class) return 6 - 3;
+ if (c2 == Byte.class) return 1 - 3;
+ if (c2 == Short.class) return 2 - 3;
+ if (c2 == BigDecimal.class) return 8 - 3;
+ if (c2 == BigInteger.class) return 5 - 3;
+ return 0;
+ }
+ if (c1 == Long.class) {
+ if (c2 == Integer.class) return 3 - 4;
+ if (c2 == Double.class) return 7 - 4;
+ if (c2 == Float.class) return 6 - 4;
+ if (c2 == Byte.class) return 1 - 4;
+ if (c2 == Short.class) return 2 - 4;
+ if (c2 == BigDecimal.class) return 8 - 4;
+ if (c2 == BigInteger.class) return 5 - 4;
+ return 0;
+ }
+ if (c1 == Double.class) {
+ if (c2 == Integer.class) return 3 - 7;
+ if (c2 == Long.class) return 4 - 7;
+ if (c2 == Float.class) return 6 - 7;
+ if (c2 == Byte.class) return 1 - 7;
+ if (c2 == Short.class) return 2 - 7;
+ if (c2 == BigDecimal.class) return 8 - 7;
+ if (c2 == BigInteger.class) return 5 - 7;
+ return 0;
+ }
+ if (c1 == Float.class) {
+ if (c2 == Integer.class) return 3 - 6;
+ if (c2 == Long.class) return 4 - 6;
+ if (c2 == Double.class) return 7 - 6;
+ if (c2 == Byte.class) return 1 - 6;
+ if (c2 == Short.class) return 2 - 6;
+ if (c2 == BigDecimal.class) return 8 - 6;
+ if (c2 == BigInteger.class) return 5 - 6;
+ return 0;
+ }
+ if (c1 == Byte.class) {
+ if (c2 == Integer.class) return 3 - 1;
+ if (c2 == Long.class) return 4 - 1;
+ if (c2 == Double.class) return 7 - 1;
+ if (c2 == Float.class) return 6 - 1;
+ if (c2 == Short.class) return 2 - 1;
+ if (c2 == BigDecimal.class) return 8 - 1;
+ if (c2 == BigInteger.class) return 5 - 1;
+ return 0;
+ }
+ if (c1 == Short.class) {
+ if (c2 == Integer.class) return 3 - 2;
+ if (c2 == Long.class) return 4 - 2;
+ if (c2 == Double.class) return 7 - 2;
+ if (c2 == Float.class) return 6 - 2;
+ if (c2 == Byte.class) return 1 - 2;
+ if (c2 == BigDecimal.class) return 8 - 2;
+ if (c2 == BigInteger.class) return 5 - 2;
+ return 0;
+ }
+ if (c1 == BigDecimal.class) {
+ if (c2 == Integer.class) return 3 - 8;
+ if (c2 == Long.class) return 4 - 8;
+ if (c2 == Double.class) return 7 - 8;
+ if (c2 == Float.class) return 6 - 8;
+ if (c2 == Byte.class) return 1 - 8;
+ if (c2 == Short.class) return 2 - 8;
+ if (c2 == BigInteger.class) return 5 - 8;
+ return 0;
+ }
+ if (c1 == BigInteger.class) {
+ if (c2 == Integer.class) return 3 - 5;
+ if (c2 == Long.class) return 4 - 5;
+ if (c2 == Double.class) return 7 - 5;
+ if (c2 == Float.class) return 6 - 5;
+ if (c2 == Byte.class) return 1 - 5;
+ if (c2 == Short.class) return 2 - 5;
+ if (c2 == BigDecimal.class) return 8 - 5;
+ return 0;
+ }
+ return 0;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
index eb2ade5..afa1cb1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
@@ -64,7 +64,7 @@ final class ReflectionCallableMemberDescriptor extends CallableMemberDescriptor
@Override
String getDeclaration() {
- return _MethodUtil.toString(member);
+ return _MethodUtils.toString(member);
}
@Override
@@ -79,7 +79,7 @@ final class ReflectionCallableMemberDescriptor extends CallableMemberDescriptor
@Override
boolean isVarargs() {
- return _MethodUtil.isVarargs(member);
+ return _MethodUtils.isVarargs(member);
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
index 12ac9e0..c655b93 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
@@ -83,14 +83,14 @@ public class RestrictedObjectWrapper extends DefaultObjectWrapper {
throw new IllegalStateException("build() can only be executed once.");
}
- RestrictedObjectWrapper singleton = DefaultObjectWrapperTCCLSingletonUtil.getSingleton(
+ RestrictedObjectWrapper singleton = DefaultObjectWrapperTCCLSingletonUtils.getSingleton(
this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, ConstructorInvoker.INSTANCE);
alreadyBuilt = true;
return singleton;
}
private static class ConstructorInvoker
- implements DefaultObjectWrapperTCCLSingletonUtil._ConstructorInvoker<RestrictedObjectWrapper, Builder> {
+ implements DefaultObjectWrapperTCCLSingletonUtils._ConstructorInvoker<RestrictedObjectWrapper, Builder> {
private static final ConstructorInvoker INSTANCE = new ConstructorInvoker();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
index d27d131..6438596 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
@@ -72,7 +72,7 @@ public final class SimpleJavaMethodModel extends SimpleMethod implements JavaMet
} catch (TemplateModelException e) {
throw e;
} catch (Exception e) {
- throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e);
+ throw _MethodUtils.newInvocationTemplateModelException(object, getMember(), e);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
index cebcaf8..b1d756c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
@@ -30,7 +30,7 @@ import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._ClassUtils;
/**
* This class is used as a base for non-overloaded method models and for constructors.
@@ -54,19 +54,19 @@ class SimpleMethod {
if (args == null) {
args = _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
}
- boolean isVarArg = _MethodUtil.isVarargs(member);
+ boolean isVarArg = _MethodUtils.isVarargs(member);
int typesLen = argTypes.length;
if (isVarArg) {
if (typesLen - 1 > args.length) {
throw new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member),
+ _MethodUtils.invocationErrorMessageStart(member),
" takes at least ", typesLen - 1,
typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
args.length, " was given.");
}
} else if (typesLen != args.length) {
throw new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member),
+ _MethodUtils.invocationErrorMessageStart(member),
" takes ", typesLen, typesLen == 1 ? " argument" : " arguments", ", but ",
args.length, " was given.");
}
@@ -148,9 +148,9 @@ class SimpleMethod {
private TemplateModelException createArgumentTypeMismatchException(
int argIdx, TemplateModel argVal, Class<?> targetType) {
_ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
- _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
+ _MethodUtils.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
new _DelayedOrdinal(argIdx + 1),
- " argument's value to the target Java type, ", _ClassUtil.getShortClassName(targetType),
+ " argument's value to the target Java type, ", _ClassUtils.getShortClassName(targetType),
". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal));
if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) {
desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
@@ -160,9 +160,9 @@ class SimpleMethod {
private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class<?> targetType) {
return new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
+ _MethodUtils.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
new _DelayedOrdinal(argIdx + 1),
- " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType),
+ " argument was null, but the target Java parameter type (", _ClassUtils.getShortClassName(targetType),
") is primitive and so can't store null.");
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
index 1b6d246..f1b54bf 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
@@ -29,7 +29,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
-import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,7 +80,7 @@ class UnsafeMethods {
NoSuchMethodException {
int brace = methodSpec.indexOf('(');
int dot = methodSpec.lastIndexOf('.', brace);
- Class clazz = _ClassUtil.forName(methodSpec.substring(0, dot));
+ Class clazz = _ClassUtils.forName(methodSpec.substring(0, dot));
String methodName = methodSpec.substring(dot + 1, brace);
String argSpec = methodSpec.substring(brace + 1, methodSpec.length() - 1);
StringTokenizer tok = new StringTokenizer(argSpec, ",");
@@ -90,7 +90,7 @@ class UnsafeMethods {
String argClassName = tok.nextToken();
argTypes[i] = (Class) primClasses.get(argClassName);
if (argTypes[i] == null) {
- argTypes[i] = _ClassUtil.forName(argClassName);
+ argTypes[i] = _ClassUtils.forName(argClassName);
}
}
return clazz.getMethod(methodName, argTypes);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ebb39b84/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
deleted file mode 100644
index 004ebaa..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.core.model.impl;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.freemarker.core._DelayedConversionToString;
-import org.apache.freemarker.core._DelayedJQuote;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- */
-public final class _MethodUtil {
-
- private _MethodUtil() {
- // Not meant to be instantiated
- }
-
- /**
- * Determines whether the type given as the 1st argument is convertible to the type given as the 2nd argument
- * for method call argument conversion. This follows the rules of the Java reflection-based method call, except
- * that since we don't have the value here, a boxed class is never seen as convertible to a primitive type.
- *
- * @return 0 means {@code false}, non-0 means {@code true}.
- * That is, 0 is returned less specificity or incomparable specificity, also when if
- * then method was aborted because of {@code ifHigherThan}.
- * The absolute value of the returned non-0 number symbolizes how more specific it is:
- * <ul>
- * <li>1: The two classes are identical</li>
- * <li>2: The 1st type is primitive, the 2nd type is the corresponding boxing class</li>
- * <li>3: Both classes are numerical, and one is convertible into the other with widening conversion.
- * E.g., {@code int} is convertible to {@code long} and {#code double}, hence {@code int} is more
- * specific.
- * This ignores primitive VS boxed mismatches, except that a boxed class is never seen as
- * convertible to a primitive class.</li>
- * <li>4: One class is {@code instanceof} of the other, but they aren't identical.
- * But unlike in Java, primitive numerical types are {@code instanceof} {@link Number} here.</li>
- * </ul>
- */
- // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages.
- static int isMoreOrSameSpecificParameterType(final Class<?> specific, final Class<?> generic, boolean bugfixed,
- int ifHigherThan) {
- if (ifHigherThan >= 4) return 0;
- if (generic.isAssignableFrom(specific)) {
- // Identity or widening reference conversion:
- return generic == specific ? 1 : 4;
- } else {
- final boolean specificIsPrim = specific.isPrimitive();
- final boolean genericIsPrim = generic.isPrimitive();
- if (specificIsPrim) {
- if (genericIsPrim) {
- if (ifHigherThan >= 3) return 0;
- return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0;
- } else { // => specificIsPrim && !genericIsPrim
- if (bugfixed) {
- final Class<?> specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific);
- if (specificAsBoxed == generic) {
- // A primitive class is more specific than its boxing class, because it can't store null
- return 2;
- } else if (generic.isAssignableFrom(specificAsBoxed)) {
- // Note: This only occurs if `specific` is a primitive numerical, and `generic == Number`
- return 4;
- } else if (ifHigherThan >= 3) {
- return 0;
- } else if (Number.class.isAssignableFrom(specificAsBoxed)
- && Number.class.isAssignableFrom(generic)) {
- return isWideningBoxedNumberConversion(specificAsBoxed, generic) ? 3 : 0;
- } else {
- return 0;
- }
- } else {
- return 0;
- }
- }
- } else { // => !specificIsPrim
- if (ifHigherThan >= 3) return 0;
- if (bugfixed && !genericIsPrim
- && Number.class.isAssignableFrom(specific) && Number.class.isAssignableFrom(generic)) {
- return isWideningBoxedNumberConversion(specific, generic) ? 3 : 0;
- } else {
- return 0;
- }
- }
- } // of: !generic.isAssignableFrom(specific)
- }
-
- private static boolean isWideningPrimitiveNumberConversion(final Class<?> source, final Class<?> target) {
- if (target == Short.TYPE && (source == Byte.TYPE)) {
- return true;
- } else if (target == Integer.TYPE &&
- (source == Short.TYPE || source == Byte.TYPE)) {
- return true;
- } else if (target == Long.TYPE &&
- (source == Integer.TYPE || source == Short.TYPE ||
- source == Byte.TYPE)) {
- return true;
- } else if (target == Float.TYPE &&
- (source == Long.TYPE || source == Integer.TYPE ||
- source == Short.TYPE || source == Byte.TYPE)) {
- return true;
- } else if (target == Double.TYPE &&
- (source == Float.TYPE || source == Long.TYPE ||
- source == Integer.TYPE || source == Short.TYPE ||
- source == Byte.TYPE)) {
- return true;
- } else {
- return false;
- }
- }
-
- private static boolean isWideningBoxedNumberConversion(final Class<?> source, final Class<?> target) {
- if (target == Short.class && source == Byte.class) {
- return true;
- } else if (target == Integer.class &&
- (source == Short.class || source == Byte.class)) {
- return true;
- } else if (target == Long.class &&
- (source == Integer.class || source == Short.class ||
- source == Byte.class)) {
- return true;
- } else if (target == Float.class &&
- (source == Long.class || source == Integer.class ||
- source == Short.class || source == Byte.class)) {
- return true;
- } else if (target == Double.class &&
- (source == Float.class || source == Long.class ||
- source == Integer.class || source == Short.class ||
- source == Byte.class)) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Attention, this doesn't handle primitive classes correctly, nor numerical conversions.
- */
- static Set<Class<?>> getAssignables(Class<?> c1, Class<?> c2) {
- Set<Class<?>> s = new HashSet<>();
- collectAssignables(c1, c2, s);
- return s;
- }
-
- private static void collectAssignables(Class<?> c1, Class<?> c2, Set<Class<?>> s) {
- if (c1.isAssignableFrom(c2)) {
- s.add(c1);
- }
- Class<?> sc = c1.getSuperclass();
- if (sc != null) {
- collectAssignables(sc, c2, s);
- }
- Class<?>[] itf = c1.getInterfaces();
- for (Class<?> anItf : itf) {
- collectAssignables(anItf, c2, s);
- }
- }
-
- public static Class<?>[] getParameterTypes(Member member) {
- if (member instanceof Method) {
- return ((Method) member).getParameterTypes();
- }
- if (member instanceof Constructor<?>) {
- return ((Constructor<?>) member).getParameterTypes();
- }
- throw new IllegalArgumentException("\"member\" must be Method or Constructor");
- }
-
- static boolean isVarargs(Member member) {
- if (member instanceof Method) {
- return ((Method) member).isVarArgs();
- }
- if (member instanceof Constructor) {
- return ((Constructor<?>) member).isVarArgs();
- }
- throw new BugException();
- }
-
- /**
- * Returns a more streamlined method or constructor description than {@code Member.toString()} does.
- */
- public static String toString(Member member) {
- if (!(member instanceof Method || member instanceof Constructor)) {
- throw new IllegalArgumentException("\"member\" must be a Method or Constructor");
- }
-
- StringBuilder sb = new StringBuilder();
-
- if ((member.getModifiers() & Modifier.STATIC) != 0) {
- sb.append("static ");
- }
-
- String className = _ClassUtil.getShortClassName(member.getDeclaringClass());
- if (className != null) {
- sb.append(className);
- sb.append('.');
- }
- sb.append(member.getName());
-
- sb.append('(');
- Class<?>[] paramTypes = _MethodUtil.getParameterTypes(member);
- for (int i = 0; i < paramTypes.length; i++) {
- if (i != 0) sb.append(", ");
- String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]);
- if (i == paramTypes.length - 1 && paramTypeDecl.endsWith("[]") && _MethodUtil.isVarargs(member)) {
- sb.append(paramTypeDecl.substring(0, paramTypeDecl.length() - 2));
- sb.append("...");
- } else {
- sb.append(paramTypeDecl);
- }
- }
- sb.append(')');
-
- return sb.toString();
- }
-
- static Object[] invocationErrorMessageStart(Member member) {
- return invocationErrorMessageStart(member, member instanceof Constructor);
- }
-
- private static Object[] invocationErrorMessageStart(Object member, boolean isConstructor) {
- return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) };
- }
-
- static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) {
- return newInvocationTemplateModelException(
- object,
- member,
- (member.getModifiers() & Modifier.STATIC) != 0,
- member instanceof Constructor,
- e);
- }
-
- static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) {
- return newInvocationTemplateModelException(
- object,
- new _DelayedConversionToString(callableMemberDescriptor) {
- @Override
- protected String doConversion(Object callableMemberDescriptor) {
- return ((CallableMemberDescriptor) callableMemberDescriptor).getDeclaration();
- }
- },
- callableMemberDescriptor.isStatic(),
- callableMemberDescriptor.isConstructor(),
- e);
- }
-
- private static TemplateModelException newInvocationTemplateModelException(
- Object parentObject, Object member, boolean isStatic, boolean isConstructor, Throwable e) {
- while (e instanceof InvocationTargetException) {
- Throwable cause = ((InvocationTargetException) e).getTargetException();
- if (cause != null) {
- e = cause;
- } else {
- break;
- }
- }
-
- return new _TemplateModelException(e,
- invocationErrorMessageStart(member, isConstructor),
- " threw an exception",
- isStatic || isConstructor ? "" : new Object[] {
- " when invoked on ", parentObject.getClass(), " object ", new _DelayedJQuote(parentObject)
- },
- "; see cause exception in the Java stack trace.");
- }
-
- /**
- * Extracts the JavaBeans property from a reader method name, or returns {@code null} if the method name doesn't
- * look like a reader method name.
- */
- static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) {
- int start;
- if (name.startsWith("get")) {
- start = 3;
- } else if (returnType == boolean.class && name.startsWith("is")) {
- start = 2;
- } else {
- return null;
- }
- int ln = name.length();
-
- if (start == ln) {
- return null;
- }
- char c1 = name.charAt(start);
-
- return start + 1 < ln && Character.isUpperCase(name.charAt(start + 1)) && Character.isUpperCase(c1)
- ? name.substring(start) // getFOOBar => "FOOBar" (not lower case) according the JavaBeans spec.
- : new StringBuilder(ln - start).append(Character.toLowerCase(c1)).append(name, start + 1, ln)
- .toString();
- }
-
-}
\ No newline at end of file