freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [39/51] [abbrv] [partial] incubator-freemarker git commit: Restructured project so that freemarker-test-utils depends on freemarker-core (and hence can provide common classes for testing templates, and can use utility classes defined in the core). As a c
Date Mon, 15 May 2017 21:23:55 GMT
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";
+        }
+        
+    }
+    
+}


Mime
View raw message