http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java new file mode 100644 index 0000000..37e4726 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java @@ -0,0 +1,585 @@ +/* + * 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 junit.framework.TestCase; + +@SuppressWarnings("boxing") +public class OverloadedNumberUtilTest extends TestCase { + + public OverloadedNumberUtilTest(String name) { + super(name); + } + + public void testIntegerCoercions() { + cipEqu(Byte.valueOf(Byte.MAX_VALUE)); + cipEqu(Byte.valueOf((byte) 0)); + cipEqu(Byte.valueOf(Byte.MIN_VALUE)); + + cipEqu(Short.valueOf(Byte.MAX_VALUE), + new OverloadedNumberUtil.ShortOrByte((short) Byte.MAX_VALUE, Byte.MAX_VALUE)); + cipEqu(Short.valueOf((short) 0), + new OverloadedNumberUtil.ShortOrByte((short) 0, (byte) 0)); + cipEqu(Short.valueOf(Byte.MIN_VALUE), + new OverloadedNumberUtil.ShortOrByte((short) Byte.MIN_VALUE, Byte.MIN_VALUE)); + + cipEqu(Short.valueOf((short) (Byte.MAX_VALUE + 1))); + cipEqu(Short.valueOf((short) (Byte.MIN_VALUE - 1))); + cipEqu(Short.valueOf(Short.MAX_VALUE)); + cipEqu(Short.valueOf(Short.MIN_VALUE)); + + cipEqu(Integer.valueOf(Byte.MAX_VALUE), + new OverloadedNumberUtil.IntegerOrByte((int) Byte.MAX_VALUE, Byte.MAX_VALUE)); + cipEqu(Integer.valueOf(0), + new OverloadedNumberUtil.IntegerOrByte(0, (byte) 0)); + cipEqu(Integer.valueOf(Byte.MIN_VALUE), + new OverloadedNumberUtil.IntegerOrByte((int) Byte.MIN_VALUE, Byte.MIN_VALUE)); + + cipEqu(Integer.valueOf(Byte.MAX_VALUE + 1), + new OverloadedNumberUtil.IntegerOrShort(Byte.MAX_VALUE + 1, (short) (Byte.MAX_VALUE + 1))); + cipEqu(Integer.valueOf(Byte.MIN_VALUE - 1), + new OverloadedNumberUtil.IntegerOrShort(Byte.MIN_VALUE - 1, (short) (Byte.MIN_VALUE - 1))); + cipEqu(Integer.valueOf(Short.MAX_VALUE), + new OverloadedNumberUtil.IntegerOrShort((int) Short.MAX_VALUE, Short.MAX_VALUE)); + cipEqu(Integer.valueOf(Short.MIN_VALUE), + new OverloadedNumberUtil.IntegerOrShort((int) Short.MIN_VALUE, Short.MIN_VALUE)); + + cipEqu(Integer.valueOf(Short.MAX_VALUE + 1)); + cipEqu(Integer.valueOf(Short.MIN_VALUE - 1)); + cipEqu(Integer.valueOf(Integer.MAX_VALUE)); + cipEqu(Integer.valueOf(Integer.MIN_VALUE)); + + cipEqu(Long.valueOf(Byte.MAX_VALUE), + new OverloadedNumberUtil.LongOrByte((long) Byte.MAX_VALUE, Byte.MAX_VALUE)); + cipEqu(Long.valueOf(0), + new OverloadedNumberUtil.LongOrByte((long) 0, (byte) 0)); + cipEqu(Long.valueOf(Byte.MIN_VALUE), + new OverloadedNumberUtil.LongOrByte((long) Byte.MIN_VALUE, Byte.MIN_VALUE)); + + cipEqu(Long.valueOf(Byte.MAX_VALUE + 1), + new OverloadedNumberUtil.LongOrShort((long) (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1))); + cipEqu(Long.valueOf(Byte.MIN_VALUE - 1), + new OverloadedNumberUtil.LongOrShort((long) (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1))); + cipEqu(Long.valueOf(Short.MAX_VALUE), + new OverloadedNumberUtil.LongOrShort((long) Short.MAX_VALUE, Short.MAX_VALUE)); + cipEqu(Long.valueOf(Short.MIN_VALUE), + new OverloadedNumberUtil.LongOrShort((long) Short.MIN_VALUE, Short.MIN_VALUE)); + + cipEqu(Long.valueOf(Short.MAX_VALUE + 1), + new OverloadedNumberUtil.LongOrInteger((long) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1)); + cipEqu(Long.valueOf(Short.MIN_VALUE - 1), + new OverloadedNumberUtil.LongOrInteger((long) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1)); + cipEqu(Long.valueOf(Integer.MAX_VALUE), + new OverloadedNumberUtil.LongOrInteger((long) Integer.MAX_VALUE, Integer.MAX_VALUE)); + cipEqu(Long.valueOf(Integer.MIN_VALUE), + new OverloadedNumberUtil.LongOrInteger((long) Integer.MIN_VALUE, Integer.MIN_VALUE)); + + cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L)); + cipEqu(Long.valueOf(Integer.MIN_VALUE - 1L)); + cipEqu(Long.valueOf(Long.MAX_VALUE)); + cipEqu(Long.valueOf(Long.MIN_VALUE)); + } + + public void testIntegerNoCoercions() { + cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), 0); + cipEqu(Integer.valueOf(0), Integer.valueOf(0), 0); + cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), 0); + } + + public void testIntegerLimitedCoercions() { + cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), TypeFlags.INTEGER); + cipEqu(Integer.valueOf(0), Integer.valueOf(0), TypeFlags.INTEGER); + cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), TypeFlags.INTEGER); + + cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L), Long.valueOf(Integer.MAX_VALUE + 1L), TypeFlags.INTEGER); + + for (int n = -1; n < 2; n++) { + final Long longN = Long.valueOf(n); + cipEqu(longN, new OverloadedNumberUtil.LongOrInteger(longN, n), TypeFlags.INTEGER); + cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n), TypeFlags.SHORT); + cipEqu(longN, new OverloadedNumberUtil.LongOrByte(longN, (byte) n), TypeFlags.BYTE); + cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n), + TypeFlags.SHORT | TypeFlags.INTEGER); + } + } + + public void testBigDecimalCoercions() { + cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123))); + cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123)), + TypeFlags.DOUBLE | TypeFlags.INTEGER); + cipEqu(new BigDecimal(123), TypeFlags.INTEGER); + cipEqu(new BigDecimal(123), TypeFlags.INTEGER | TypeFlags.LONG); + cipEqu(new BigDecimal(123), TypeFlags.DOUBLE); + cipEqu(new BigDecimal(123), TypeFlags.DOUBLE | TypeFlags.FLOAT); + + cipEqu(new BigDecimal(123.5)); + // Not wasting time with check if it's a whole number if we only have integer-only or non-integer-only targets: + cipEqu(new BigDecimal(123.5), TypeFlags.INTEGER | TypeFlags.LONG); + cipEqu(new BigDecimal(123.5), TypeFlags.DOUBLE | TypeFlags.FLOAT); + + cipEqu(new BigDecimal("0.01")); + cipEqu(new BigDecimal("-0.01")); + cipEqu(BigDecimal.ZERO, new OverloadedNumberUtil.IntegerBigDecimal(BigDecimal.ZERO)); + } + + public void testUnknownNumberCoercion() { + cipEqu(new RationalNumber(2, 3)); + } + + @SuppressWarnings("boxing") + public void testDoubleCoercion() { + cipEqu(Double.valueOf(1.5), new OverloadedNumberUtil.DoubleOrFloat(1.5)); + cipEqu(Double.valueOf(-0.125), new OverloadedNumberUtil.DoubleOrFloat(-0.125)); + cipEqu(Double.valueOf(Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) Float.MAX_VALUE)); + cipEqu(Double.valueOf(-Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) -Float.MAX_VALUE)); + cipEqu(Double.valueOf(Float.MAX_VALUE * 10.0)); + cipEqu(Double.valueOf(-Float.MAX_VALUE * 10.0)); + + cipEqu(Double.valueOf(0), new OverloadedNumberUtil.DoubleOrByte(0.0, (byte) 0)); + cipEqu(Double.valueOf(Byte.MAX_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MAX_VALUE, Byte.MAX_VALUE)); + cipEqu(Double.valueOf(Byte.MIN_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MIN_VALUE, Byte.MIN_VALUE)); + + cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double) + (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1))); + cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double) + (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1))); + + cipEqu(Double.valueOf(Short.MAX_VALUE + 1), + new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1)); + cipEqu(Double.valueOf(Short.MIN_VALUE - 1), + new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1)); + cipEqu(Double.valueOf(16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216.0, 16777216)); + cipEqu(Double.valueOf(-16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216.0, -16777216)); + + cipEqu(Double.valueOf(Integer.MAX_VALUE), + new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MAX_VALUE, Integer.MAX_VALUE)); + cipEqu(Double.valueOf(Integer.MIN_VALUE), + new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MIN_VALUE, Integer.MIN_VALUE)); + + cipEqu(Double.valueOf(Integer.MAX_VALUE + 1L), + new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MAX_VALUE + 1L), Integer.MAX_VALUE + 1L)); + cipEqu(Double.valueOf(Integer.MIN_VALUE - 1L), + new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MIN_VALUE - 1L), Integer.MIN_VALUE - 1L)); + cipEqu(Double.valueOf(Long.MAX_VALUE), + new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE)); + cipEqu(Double.valueOf(Long.MIN_VALUE), + new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE)); + + // When only certain target types are present: + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE | TypeFlags.SHORT); + cipEqu(Double.valueOf(-129), TypeFlags.BYTE); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrShort(5.0, (short) 5), TypeFlags.SHORT); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrInteger(5.0, 5), TypeFlags.INTEGER); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrLong(5.0, 5), TypeFlags.LONG); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0), TypeFlags.FLOAT); + cipEqu(Double.valueOf(5), Double.valueOf(5), TypeFlags.DOUBLE); + cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(Double.valueOf(5.9), + TypeFlags.DOUBLE | TypeFlags.INTEGER); + cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9), + TypeFlags.FLOAT | TypeFlags.INTEGER); + cipEqu(Double.valueOf(5.9), TypeFlags.INTEGER); + cipEqu(Double.valueOf(Long.MAX_VALUE), + new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(Double.valueOf(Long.MAX_VALUE), + TypeFlags.DOUBLE | TypeFlags.LONG); + cipEqu(Double.valueOf(Long.MIN_VALUE), + new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(Double.valueOf(Float.MAX_VALUE * 10), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(Double.valueOf(-Float.MAX_VALUE * 10), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + + // Rounded values: + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0)); + cipEqu(Double.valueOf(-0.0000009), + new OverloadedNumberUtil.DoubleOrByte(-0.0000009, (byte) 0)); + cipEqu(Double.valueOf(0.9999991), + new OverloadedNumberUtil.DoubleOrByte(0.9999991, (byte) 1)); + cipEqu(Double.valueOf(-0.9999991), + new OverloadedNumberUtil.DoubleOrByte(-0.9999991, (byte) -1)); + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0), + TypeFlags.SHORT | TypeFlags.DOUBLE); + cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0), + TypeFlags.INTEGER | TypeFlags.DOUBLE); + cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0), + TypeFlags.LONG | TypeFlags.DOUBLE); + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0), TypeFlags.BYTE); + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0), TypeFlags.SHORT); + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0), TypeFlags.INTEGER); + cipEqu(Double.valueOf(0.0000009), + new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0L), TypeFlags.LONG); + cipEqu(Double.valueOf(0.9999999), + new OverloadedNumberUtil.DoubleOrInteger(0.9999999, 1), TypeFlags.INTEGER); + cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9e-6), + new OverloadedNumberUtil.DoubleOrByte(Byte.MAX_VALUE + 0.9e-6, Byte.MAX_VALUE)); + cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9e-6), + new OverloadedNumberUtil.DoubleOrByte(Byte.MIN_VALUE - 0.9e-6, Byte.MIN_VALUE)); + cipEqu(Double.valueOf(Byte.MAX_VALUE + 1.1e-6), + new OverloadedNumberUtil.DoubleOrFloat(Byte.MAX_VALUE + 1.1e-6)); + cipEqu(Double.valueOf(Byte.MIN_VALUE - 1.1e-6), + new OverloadedNumberUtil.DoubleOrFloat(Byte.MIN_VALUE - 1.1e-6)); + cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9999991), + new OverloadedNumberUtil.DoubleOrShort( + Byte.MAX_VALUE + 0.9999991, (short) (Byte.MAX_VALUE + 1))); + cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9999991), + new OverloadedNumberUtil.DoubleOrShort( + Byte.MIN_VALUE - 0.9999991, (short) (Byte.MIN_VALUE - 1))); + + cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double) + (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1))); + cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double) + (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1))); + + cipEqu(Short.MAX_VALUE + 0.9999991, + new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MAX_VALUE + 0.9999991, Short.MAX_VALUE + 1)); + cipEqu(Short.MIN_VALUE - 0.9999991, + new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MIN_VALUE - 0.9999991, Short.MIN_VALUE - 1)); + cipEqu(16777216 + 0.9e-6, + new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216 + 0.9e-6, 16777216)); + cipEqu(-16777216 - 0.9e-6, + new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216 - 0.9e-6, -16777216)); + + cipEqu(Integer.MAX_VALUE + 0.9e-6, + new OverloadedNumberUtil.DoubleOrInteger(Integer.MAX_VALUE + 0.9e-6, Integer.MAX_VALUE)); + cipEqu(Integer.MIN_VALUE - 0.9e-6, + new OverloadedNumberUtil.DoubleOrInteger(Integer.MIN_VALUE - 0.9e-6, Integer.MIN_VALUE)); + + cipEqu(Integer.MAX_VALUE + 1L + 0.9e-6, + new OverloadedNumberUtil.DoubleOrFloat(Integer.MAX_VALUE + 1L + 0.9e-6)); + cipEqu(Integer.MIN_VALUE - 1L - 0.9e-6, + new OverloadedNumberUtil.DoubleOrFloat(Integer.MIN_VALUE - 1L - 0.9e-6)); + cipEqu(Long.MAX_VALUE + 0.9e-6, + new OverloadedNumberUtil.DoubleOrFloat(Long.MAX_VALUE + 0.9e-6)); + cipEqu(Long.MIN_VALUE - 0.9e-6, + new OverloadedNumberUtil.DoubleOrFloat(Long.MIN_VALUE - 0.9e-6)); + } + + @SuppressWarnings("boxing") + public void testFloatCoercion() { + cipEqu(1.00002f); + cipEqu(-1.00002f); + cipEqu(1.999989f); + cipEqu(-1.999989f); + cipEqu(16777218f); + cipEqu(-16777218f); + + cipEqu(1f, new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1)); + cipEqu(-1f, new OverloadedNumberUtil.FloatOrByte(-1f, (byte) -1)); + cipEqu(1.000009f, new OverloadedNumberUtil.FloatOrByte(1.000009f, (byte) 1)); + cipEqu(-1.000009f, new OverloadedNumberUtil.FloatOrByte(-1.000009f, (byte) -1)); + cipEqu(1.999991f, new OverloadedNumberUtil.FloatOrByte(1.999991f, (byte) 2)); + cipEqu(-1.999991f, new OverloadedNumberUtil.FloatOrByte(-1.999991f, (byte) -2)); + + cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000)); + cipEqu(-1000f, new OverloadedNumberUtil.FloatOrShort(-1000f, (short) -1000)); + cipEqu(1000.00006f); + + cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000)); + cipEqu(-60000f, new OverloadedNumberUtil.FloatOrInteger(-60000f, -60000)); + cipEqu(60000.004f); + + cipEqu(100f, new OverloadedNumberUtil.FloatOrByte(100f, (byte) 100), TypeFlags.MASK_KNOWN_INTEGERS); + cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000), TypeFlags.MASK_KNOWN_INTEGERS); + cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.MASK_KNOWN_INTEGERS); + cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.LONG); + cipEqu((float) Integer.MAX_VALUE, (float) Integer.MAX_VALUE, TypeFlags.LONG); + cipEqu((float) -Integer.MAX_VALUE, (float) -Integer.MAX_VALUE); + + cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE); + } + + public void testBigIntegerCoercion() { + BigInteger bi; + + cipEqu(BigInteger.ZERO, new OverloadedNumberUtil.BigIntegerOrByte(BigInteger.ZERO)); + bi = new BigInteger(String.valueOf(Byte.MAX_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi)); + bi = new BigInteger(String.valueOf(Byte.MIN_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi)); + + bi = new BigInteger(String.valueOf(Byte.MAX_VALUE + 1)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi)); + bi = new BigInteger(String.valueOf(Byte.MIN_VALUE - 1)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi)); + bi = new BigInteger(String.valueOf(Short.MAX_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi)); + bi = new BigInteger(String.valueOf(Short.MIN_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi)); + + bi = new BigInteger(String.valueOf(Short.MAX_VALUE + 1)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi)); + bi = new BigInteger(String.valueOf(Short.MIN_VALUE - 1)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi)); + bi = new BigInteger(String.valueOf(Integer.MAX_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi)); + bi = new BigInteger(String.valueOf(Integer.MIN_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi)); + + bi = new BigInteger(String.valueOf(Integer.MAX_VALUE + 1L)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi)); + bi = new BigInteger(String.valueOf(Integer.MIN_VALUE - 1L)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi)); + bi = new BigInteger(String.valueOf(Long.MAX_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi)); + bi = new BigInteger(String.valueOf(Long.MIN_VALUE)); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi)); + + cipEqu(new BigInteger(String.valueOf(Long.MAX_VALUE)).add(BigInteger.ONE)); + cipEqu(new BigInteger(String.valueOf(Long.MIN_VALUE)).subtract(BigInteger.ONE)); + + bi = new BigInteger("0"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), + TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), + TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), + TypeFlags.FLOAT); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), + TypeFlags.DOUBLE); + + bi = new BigInteger("16777215"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + bi = new BigInteger("-16777215"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + + bi = new BigInteger("16777216"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + bi = new BigInteger("-16777216"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + + bi = new BigInteger("16777217"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, TypeFlags.FLOAT); + bi = new BigInteger("-16777217"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, TypeFlags.FLOAT); + + bi = new BigInteger("9007199254740991"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE); + bi = new BigInteger("-9007199254740991"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE); + + bi = new BigInteger("9007199254740992"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE); + bi = new BigInteger("-9007199254740992"); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE); + + bi = new BigInteger("9007199254740993"); + cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(bi, TypeFlags.FLOAT); + cipEqu(bi, TypeFlags.DOUBLE); + bi = new BigInteger("-9007199254740993"); + cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS); + cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT); + cipEqu(bi, TypeFlags.FLOAT); + cipEqu(bi, TypeFlags.DOUBLE); + + bi = new BigInteger("9007199254740994"); + cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS); + bi = new BigInteger("-9007199254740994"); + cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS); + } + + private void cipEqu(Number actualAndExpected) { + cipEqu(actualAndExpected, actualAndExpected, -1); + } + + private void cipEqu(Number actual, Number expected) { + cipEqu(actual, expected, -1); + } + + private void cipEqu(Number actualAndExpected, int flags) { + cipEqu(actualAndExpected, actualAndExpected, flags); + } + + @SuppressWarnings("boxing") + private void cipEqu(Number actual, Number expected, int flags) { + Number res = OverloadedNumberUtil.addFallbackType(actual, flags); + assertEquals(expected.getClass(), res.getClass()); + assertEquals(expected, res); + + // Some number types wrap the number with multiple types and equals() only compares one of them. So we try to + // catch any inconsistency: + assertEquals(expected.byteValue(), res.byteValue()); + assertEquals(expected.shortValue(), res.shortValue()); + assertEquals(expected.intValue(), res.intValue()); + assertEquals(expected.longValue(), res.longValue()); + assertEquals(expected.floatValue(), res.floatValue()); + assertEquals(expected.doubleValue(), res.doubleValue()); + } + + public void testGetArgumentConversionPrice() { + // Unknown number types: + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + RationalNumber.class, Integer.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, RationalNumber.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + RationalNumber.class, Float.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + Float.class, RationalNumber.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + RationalNumber.class, RationalNumber.class)); + + // Fully check some rows (not all of them; the code is generated anyways): + + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Byte.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Short.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Integer.class)); + assertEquals(10004, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Long.class)); + assertEquals(10005, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, BigInteger.class)); + assertEquals(30006, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Float.class)); + assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, Double.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, BigDecimal.class)); + + assertEquals(45001, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Byte.class)); + assertEquals(44002, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Short.class)); + assertEquals(41003, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Integer.class)); + assertEquals(41004, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Long.class)); + assertEquals(40005, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, BigInteger.class)); + assertEquals(33006, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Float.class)); + assertEquals(32007, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, Double.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, BigDecimal.class)); + + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Byte.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Short.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Integer.class)); + assertEquals(21004, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Long.class)); + assertEquals(21005, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, BigInteger.class)); + assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Float.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, Double.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class)); + + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Byte.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Short.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Integer.class)); + assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Long.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, BigInteger.class)); + assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Float.class)); + assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, Double.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class)); + + // Check if all fromC is present: + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Byte.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Short.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Integer.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Long.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + BigInteger.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Float.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + Double.class, BigDecimal.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + BigDecimal.class, BigDecimal.class)); + assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.IntegerBigDecimal.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrFloat.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrShort.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.FloatOrShort.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.FloatOrInteger.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrByte.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrIntegerOrFloat.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrInteger.class, BigDecimal.class)); + assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrByte.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrShort.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrInteger.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrLong.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrFloat.class, BigDecimal.class)); + assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice( + OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class)); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java new file mode 100644 index 0000000..3e6c2ed --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java @@ -0,0 +1,445 @@ +/* + * 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.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.freemarker.core.util._NumberUtil; + +import junit.framework.TestCase; + +@SuppressWarnings("boxing") +public class ParameterListPreferabilityTest extends TestCase { + + public ParameterListPreferabilityTest(String name) { + super(name); + } + + public void testNumberical() { + // Note: the signature lists consists of the same elements, only their order changes depending on the type + // of the argument value. + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { byte.class }, + new Class[] { Byte.class }, + new Class[] { short.class }, + new Class[] { Short.class }, + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { BigInteger.class }, + new Class[] { float.class }, + new Class[] { Float.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { BigDecimal.class }, + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class } + }, + new Object[] { (byte) 1 }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { BigDecimal.class }, + new Class[] { BigInteger.class }, + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { float.class }, + new Class[] { Float.class }, + new Class[] { short.class }, + new Class[] { Short.class }, + new Class[] { byte.class }, + new Class[] { Byte.class }, + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class }, + }, + new Object[] { new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal("1")) }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { BigDecimal.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { float.class }, + new Class[] { Float.class }, + new Class[] { BigInteger.class }, + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { short.class }, + new Class[] { Short.class }, + new Class[] { byte.class }, + new Class[] { Byte.class }, + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class }, + }, + new Object[] { new BigDecimal("1") /* possibly non-integer */ }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { float.class }, + new Class[] { Float.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { BigDecimal.class }, + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { short.class }, + new Class[] { Short.class }, + new Class[] { byte.class }, + new Class[] { Byte.class }, + new Class[] { BigInteger.class }, + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class }, + }, + new Object[] { new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1) }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { BigInteger.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { BigDecimal.class }, + new Class[] { short.class }, + new Class[] { Short.class }, + new Class[] { float.class }, + new Class[] { Float.class }, + + // Two incompatibles removed, would be removed in reality: + new Class[] { byte.class }, + new Class[] { Byte.class }, + + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class } + }, + new Object[] { new OverloadedNumberUtil.IntegerOrShort(1, (short) 1) }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { long.class }, + new Class[] { Long.class }, + new Class[] { BigInteger.class }, + new Class[] { BigDecimal.class }, + new Class[] { double.class }, + new Class[] { Double.class }, + new Class[] { float.class }, + new Class[] { Float.class }, + // skip byte and short as the would be equal with int (all invalid target types) + new Class[] { int.class }, // In reality, this and Integer are removed as not-applicable overloads + new Class[] { Integer.class }, + new Class[] { Number.class }, + new Class[] { Serializable.class }, + new Class[] { Object.class } + }, + new Object[] { 1L }); + + // Undecidable comparisons: + + testAllCmpPermutationsEqu( + new Class[][] { + new Class[] { Byte.class }, + new Class[] { Short.class }, + new Class[] { Integer.class }, + new Class[] { Long.class }, + new Class[] { BigInteger.class }, + new Class[] { Float.class }, + }, + new Object[] { 1.0 }); + + testAllCmpPermutationsEqu( + new Class[][] { + new Class[] { byte.class }, + new Class[] { short.class }, + new Class[] { int.class }, + new Class[] { long.class }, + new Class[] { float.class }, + }, + new Object[] { 1.0 }); + } + + public void testPrimitiveIsMoreSpecific() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { boolean.class }, + new Class[] { Boolean.class } + }, + new Object[] { true }); + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { char.class }, + new Class[] { Character.class } + }, + new Object[] { 'x' }); + } + + public void testCharIsMoreSpecificThanString() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { char.class }, + new Class[] { Character.class }, + new Class[] { String.class }, + new Class[] { CharSequence.class } + }, + new Object[] { "s" }); + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { char.class }, + new Class[] { Character.class }, + new Class[] { String.class } + }, + new Object[] { 'c' }); + } + + public void testClassHierarchy() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { LinkedHashMap.class }, + new Class[] { HashMap.class }, + new Class[] { Map.class }, + new Class[] { Object.class } + }, + new Object[] { new LinkedHashMap() }); + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { LinkedHashMap.class }, + new Class[] { Cloneable.class }, + new Class[] { Object.class } + }, + new Object[] { new LinkedHashMap() }); + } + + public void testNumericalWithNonNumerical() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { Comparable.class }, + new Class[] { Object.class }, + }, + new Object[] { 1 }); + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { int.class }, + new Class[] { Integer.class }, + new Class[] { char.class }, + new Class[] { Character.class }, + }, + new Object[] { 1 }); + } + + public void testUnrelated() { + testAllCmpPermutationsEqu( + new Class[][] { + new Class[] { Serializable.class }, + new Class[] { CharSequence.class }, + new Class[] { Comparable.class } + }, + new Object[] { "s" }); + + testAllCmpPermutationsEqu( + new Class[][] { + new Class[] { HashMap.class }, + new Class[] { TreeMap.class } + }, + new Object[] { null }); + } + + public void testMultiParameter() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, String.class, String.class }, + + new Class[] { String.class, String.class, Object.class }, + new Class[] { String.class, Object.class, String.class }, + new Class[] { Object.class, String.class, String.class }, + + new Class[] { String.class, Object.class, Object.class }, + new Class[] { Object.class, String.class, Object.class }, + new Class[] { Object.class, Object.class, String.class }, + + new Class[] { Object.class, Object.class, Object.class }, + }, + new Object[] { "a", "b", "c" }); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, String.class }, + new Class[] { String.class, Object.class }, + new Class[] { CharSequence.class, CharSequence.class }, + new Class[] { CharSequence.class, Object.class }, + new Class[] { Object.class, String.class }, + new Class[] { Object.class, CharSequence.class }, + new Class[] { Object.class, Object.class }, + }, + new Object[] { "a", "b" }); + + // Subclassing is more important than primitive-VS-boxed: + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { boolean.class, boolean.class, boolean.class, String.class }, + new Class[] { Boolean.class, boolean.class, boolean.class, String.class }, + new Class[] { boolean.class, Boolean.class, Boolean.class, String.class }, + new Class[] { Boolean.class, boolean.class, Boolean.class, String.class }, + new Class[] { Boolean.class, Boolean.class, boolean.class, String.class }, + new Class[] { Boolean.class, Boolean.class, Boolean.class, String.class }, + new Class[] { Boolean.class, Boolean.class, Boolean.class, CharSequence.class }, + new Class[] { boolean.class, boolean.class, boolean.class, Object.class }, + new Class[] { Boolean.class, boolean.class, boolean.class, Object.class }, + new Class[] { boolean.class, Boolean.class, Boolean.class, Object.class }, + new Class[] { Boolean.class, boolean.class, Boolean.class, Object.class }, + new Class[] { Boolean.class, Boolean.class, boolean.class, Object.class }, + new Class[] { Boolean.class, Boolean.class, Boolean.class, Object.class }, + }, + new Object[] { true, false, true, "a" }); + + // Subclassing is more important than primitive-VS-boxed: + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { int.class, int.class, int.class, String.class }, + new Class[] { Integer.class, int.class, int.class, String.class }, + new Class[] { int.class, Integer.class, Integer.class, String.class }, + new Class[] { Integer.class, int.class, Integer.class, String.class }, + new Class[] { Integer.class, Integer.class, int.class, String.class }, + new Class[] { Integer.class, Integer.class, Integer.class, String.class }, + new Class[] { Integer.class, Integer.class, Integer.class, CharSequence.class }, + new Class[] { int.class, int.class, int.class, Object.class }, + new Class[] { Integer.class, int.class, int.class, Object.class }, + new Class[] { int.class, Integer.class, Integer.class, Object.class }, + new Class[] { Integer.class, int.class, Integer.class, Object.class }, + new Class[] { Integer.class, Integer.class, int.class, Object.class }, + new Class[] { Integer.class, Integer.class, Integer.class, Object.class }, + }, + new Object[] { 1, 2, 3, "a" }); + } + + public void testVarargs() { + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, String[].class }, + new Class[] { String.class, CharSequence[].class }, + new Class[] { String.class, Object[].class }, + }, + new Object[] { "a", "b", "c" }, + true); + + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, int[].class }, + new Class[] { String.class, Integer[].class }, + new Class[] { String.class, long[].class }, + new Class[] { String.class, Long[].class }, + new Class[] { String.class, double[].class }, + new Class[] { String.class, Double[].class }, + new Class[] { String.class, Serializable[].class }, + new Class[] { String.class, Object[].class }, + }, + new Object[] { "a", 1, 2, 3 }, + true); + + // 0-long varargs list; in case of ambiguity, the varargs component type decides: + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, Object[].class }, + new Class[] { CharSequence.class, int[].class }, + new Class[] { CharSequence.class, Integer[].class }, + new Class[] { CharSequence.class, long[].class }, + new Class[] { CharSequence.class, Long[].class }, + new Class[] { CharSequence.class, double[].class }, + new Class[] { CharSequence.class, Double[].class }, + new Class[] { CharSequence.class, Serializable[].class }, + new Class[] { CharSequence.class, Object[].class }, + new Class[] { Object.class, int[].class }, + }, + new Object[] { "a" }, + true); + + + // Different oms prefix length; in the case of ambiguity, the one with higher oms param count wins. + testAllCmpPermutationsInc( + new Class[][] { + new Class[] { String.class, int.class, int.class, int[].class }, + new Class[] { String.class, int.class, int[].class }, + new Class[] { String.class, int[].class }, + }, + new Object[] { "a", 1, 2, 3 }, + true); + } + + private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args) { + testAllCmpPermutationsInc(sortedSignatures, args, false); + } + + /** + * Compares all items with all other items in the provided descending sorted array of signatures, checking that + * for all valid indexes i and j, where j > i, it stands that sortedSignatures[i] > sortedSignatures[j]. + * The comparisons are done with both operand orders, also each items is compared to itself too. + * + * @param sortedSignatures method signatures sorted by decreasing specificity + */ + private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args, boolean varargs) { + final ArgumentTypes argTs = new ArgumentTypes(args); + for (int i = 0; i < sortedSignatures.length; i++) { + for (int j = 0; j < sortedSignatures.length; j++) { + assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]", + _NumberUtil.getSignum( + Integer.valueOf(j).compareTo(i)), + _NumberUtil.getSignum( + argTs.compareParameterListPreferability( + sortedSignatures[i], sortedSignatures[j], varargs))); + } + } + } + + private void testAllCmpPermutationsEqu(Class[][] signatures, Object[] args) { + final ArgumentTypes argTs = new ArgumentTypes(args); + for (int i = 0; i < signatures.length; i++) { + for (int j = 0; j < signatures.length; j++) { + assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]", + 0, + argTs.compareParameterListPreferability(signatures[i], signatures[j], false)); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java new file mode 100644 index 0000000..4f4c0a1 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java @@ -0,0 +1,43 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateModelException; + +public class PrallelObjectIntrospectionTest extends AbstractParallelIntrospectionTest { + + public PrallelObjectIntrospectionTest(String name) { + super(name); + } + + public static void main(String[] args) { + new PrallelObjectIntrospectionTest("almostForever") + .testReliability(Integer.MAX_VALUE); + } + + @Override + protected TemplateHashModel getWrappedEntity(int objIdx) + throws TemplateModelException { + return (TemplateHashModel) getObjectWrapper().wrap( + ManyObjectsOfDifferentClasses.OBJECTS[objIdx]); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java new file mode 100644 index 0000000..836d559 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateModelException; + +public class PrallelStaticIntrospectionTest extends AbstractParallelIntrospectionTest { + + private static final String STATICS_CLASS_CONTAINER_CLASS_NAME + = ManyStaticsOfDifferentClasses.class.getName(); + + public PrallelStaticIntrospectionTest(String name) { + super(name); + } + + public static void main(String[] args) { + new PrallelStaticIntrospectionTest("almostForever") + .testReliability(Integer.MAX_VALUE); + } + + @Override + protected TemplateHashModel getWrappedEntity(int clIdx) + throws TemplateModelException { + return (TemplateHashModel) getObjectWrapper().getStaticModels().get( + STATICS_CLASS_CONTAINER_CLASS_NAME + "$C" + + clIdx); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java new file mode 100644 index 0000000..806a186 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java @@ -0,0 +1,90 @@ +/* + * 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; + +public final class RationalNumber extends Number { + + final int divident; + final int divisor; + + public RationalNumber(int divident, int divisor) { + this.divident = divident; + this.divisor = divisor; + } + + @Override + public int intValue() { + return divident / divisor; + } + + @Override + public long longValue() { + return divident / (long) divisor; + } + + @Override + public float floatValue() { + return (float) (divident / (double) divisor); + } + + @Override + public double doubleValue() { + return divident / (double) divisor; + } + + public int getDivident() { + return divident; + } + + public int getDivisor() { + return divisor; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + divident; + result = prime * result + divisor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RationalNumber other = (RationalNumber) obj; + if (divident != other.divident) + return false; + if (divisor != other.divisor) + return false; + return true; + } + + @Override + public String toString() { + return divident + "/" + divisor; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java new file mode 100644 index 0000000..609d632 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java @@ -0,0 +1,91 @@ +/* + * 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 static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateMethodModelEx; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateScalarModel; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class StaticModelsTest { + + @Test + public void modelCaching() throws Exception { + DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).usePrivateCaches(true) + .build(); + TemplateHashModel statics = ow.getStaticModels(); + TemplateHashModel s = (TemplateHashModel) statics.get(S.class.getName()); + assertNotNull(s); + assertNotNull(s.get("F")); + assertNotNull(s.get("m")); + try { + s.get("x"); + fail(); + } catch (TemplateModelException e) { + assertThat(e.getMessage(), containsString("No such key")); + } + + try { + statics.get("no.such.ClassExists"); + fail(); + } catch (TemplateModelException e) { + assertTrue(e.getCause() instanceof ClassNotFoundException); + } + + TemplateModel f = s.get("F"); + assertTrue(f instanceof TemplateScalarModel); + assertEquals(((TemplateScalarModel) f).getAsString(), "F OK"); + + TemplateModel m = s.get("m"); + assertTrue(m instanceof TemplateMethodModelEx); + assertEquals(((TemplateScalarModel) ((TemplateMethodModelEx) m).exec(new ArrayList())).getAsString(), "m OK"); + + assertSame(s, statics.get(S.class.getName())); + + ow.clearClassIntrospecitonCache(); + TemplateHashModel sAfterClean = (TemplateHashModel) statics.get(S.class.getName()); + assertNotSame(s, sAfterClean); + assertSame(sAfterClean, statics.get(S.class.getName())); + assertNotNull(sAfterClean.get("F")); + assertNotNull(sAfterClean.get("m")); + } + + public static class S { + + public static final String F = "F OK"; + + public static String m() { + return "m OK"; + } + + } + +}
