blob: 690855450eef3761787e1a4317e2a38de985952e [file] [log] [blame]
package freemarker.ext.beans;
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 freemarker.template.utility.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 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 fixed prefix length; in the case of ambiguity, the one with higher fixed 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, true);
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, true);
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));
}
}
}
}