Author: henrib Date: Fri Aug 10 12:36:50 2012 New Revision: 1371687 URL: http://svn.apache.org/viewvc?rev=1371687&view=rev Log: Fix an edge case of ambiguous method matching (see http://apache-commons.680414.n4.nabble.com/jexl-mathod-within-namespace-not-found-if-parameter-is-int-tt4637888.html)
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java?rev=1371687&r1=1371686&r2=1371687&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java Fri Aug 10 12:36:50 2012 @@ -70,7 +70,7 @@ public final class MethodKey { * correctly. * </p> * @param parm a may-be primitive type class - * @return the equivalent object class + * @return the equivalent object class */ static Class<?> primitiveClass(Class<?> parm) { // it is marginally faster to get from the map than call isPrimitive... @@ -78,7 +78,7 @@ public final class MethodKey { Class<?> prim = PRIMITIVE_TYPES.get(parm); return prim == null ? parm : prim; } - + /** The hash code. */ private final int hashCode; /** The method name. */ @@ -530,11 +530,14 @@ public final class MethodKey { if (c2.length > c1.length) { return LESS_SPECIFIC; } + // same length, keep ultimate param offset for vararg checks + final int length = c1.length; + final int ultimate = c1.length - 1; // ok, move on and compare those of equal lengths - for (int i = 0; i < c1.length; ++i) { + for (int i = 0; i < length; ++i) { if (c1[i] != c2[i]) { - boolean last = (i == c1.length - 1); + boolean last = (i == ultimate); c1MoreSpecific = c1MoreSpecific || isStrictConvertible(c2[i], c1[i], last); c2MoreSpecific = c2MoreSpecific || isStrictConvertible(c1[i], c2[i], last); } @@ -553,11 +556,12 @@ public final class MethodKey { // attempt to choose by picking the one with the greater number of primitives or latest primitive parameter int primDiff = 0; - for (int c = 0; c < c1.length; ++c) { - if (c1[c].isPrimitive()) { + for (int c = 0; c < length; ++c) { + boolean last = (c == ultimate); + if (isPrimitive(c1[c], last)) { primDiff += 1 << c; } - if (c2[c].isPrimitive()) { + if (isPrimitive(c2[c], last)) { primDiff -= 1 << c; } } @@ -574,6 +578,24 @@ public final class MethodKey { } /** + * Checks whether a parameter class is a primitive. + * @param c the parameter class + * @param possibleVararg true if this is the last parameter which can tbe be a primitive array (vararg call) + * @return true if primitive, false otherwise + */ + private boolean isPrimitive(Class<?> c, boolean possibleVarArg) { + if (c != null) { + if (c.isPrimitive()) { + return true; + } else if (possibleVarArg) { + Class<?> t = c.getComponentType(); + return t != null && t.isPrimitive(); + } + } + return false; + } + + /** * Returns all methods that are applicable to actual argument types. * * @param methods list of all candidate methods @@ -661,8 +683,7 @@ public final class MethodKey { * in the method declaration * @return see isMethodInvocationConvertible. */ - private boolean isConvertible(Class<?> formal, Class<?> actual, - boolean possibleVarArg) { + private boolean isConvertible(Class<?> formal, Class<?> actual, boolean possibleVarArg) { // if we see Void.class, the argument was null return isInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); } @@ -676,8 +697,7 @@ public final class MethodKey { * in the method declaration * @return see isStrictMethodInvocationConvertible. */ - private boolean isStrictConvertible(Class<?> formal, Class<?> actual, - boolean possibleVarArg) { + private boolean isStrictConvertible(Class<?> formal, Class<?> actual, boolean possibleVarArg) { // if we see Void.class, the argument was null return isStrictInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java?rev=1371687&r1=1371686&r2=1371687&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Fri Aug 10 12:36:50 2012 @@ -40,7 +40,6 @@ public class IssuesTest extends JexlTest java.util.logging.Logger.getLogger(JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE); } - // JEXL-49: blocks not parsed (fixed) public void test49() throws Exception { JexlEngine jexl = new Engine(); @@ -197,11 +196,11 @@ public class IssuesTest extends JexlTest ctxt.set("a", null); String[] exprs = { - //"10 + null", - //"a - 10", - //"b * 10", - "a % b"//, - //"1000 / a" + "10 + null", + "a - 10", + "b * 10", + "a % b", + "1000 / a" }; for (int e = 0; e < exprs.length; ++e) { try { @@ -814,16 +813,16 @@ public class IssuesTest extends JexlTest JexlEngine jexl = new JexlBuilder().arithmetic(new Arithmetic132()).namespaces(ns).create(); Object evaluate = jexl.createExpression("1/0").evaluate(null); - assertTrue(Double.isInfinite((Double)evaluate)); + assertTrue(Double.isInfinite((Double) evaluate)); evaluate = jexl.createExpression("-1/0").evaluate(null); - assertTrue(Double.isInfinite((Double)evaluate)); + assertTrue(Double.isInfinite((Double) evaluate)); evaluate = jexl.createExpression("1.0/0.0").evaluate(null); - assertTrue(Double.isInfinite((Double)evaluate)); + assertTrue(Double.isInfinite((Double) evaluate)); evaluate = jexl.createExpression("-1.0/0.0").evaluate(null); - assertTrue(Double.isInfinite((Double)evaluate)); + assertTrue(Double.isInfinite((Double) evaluate)); evaluate = jexl.createExpression("math:abs(-42)").evaluate(null); assertEquals(42, evaluate); @@ -875,7 +874,6 @@ public class IssuesTest extends JexlTest assertEquals(42, result); } - @Test public void test136() throws Exception { JexlEngine jexl = new Engine(); @@ -894,4 +892,42 @@ public class IssuesTest extends JexlTest result = expr.evaluate(jc); assertEquals("EXPR01 result", 22, result); } + + public static class Context137 extends MapContext implements JexlContext.NamespaceResolver { + public static void log(Object fmt, Object... arr) { + + System.out.println(String.format(fmt.toString(), arr)); + } + + public static void log(Object fmt, int... arr) { + System.out.println(String.format(fmt.toString(), arr)); + } + + @Override + public Object resolveNamespace(String name) { + return this; + } + } + + @Test + public void test137() throws Exception { + String[] SCRIPTS = { + "var x = null; log('x = %s', x);", + "var x = 'abc'; log('x = %s', x);", + "var x = 333; log('x = %s', x);", + "var x = [1, 2]; log('x = %s', x);", + "var x = ['a', 'b']; log('x = %s', x);", + "var x = {1:'A', 2:'B'}; log('x = %s', x);" + }; + JexlEngine jexl = new JexlBuilder().create(); + JexlContext jc = new Context137(); + JexlScript script; + + for (String stext : SCRIPTS) { + System.out.println(stext); + script = jexl.createScript(stext); + script.execute(jc); + } + + } }