Author: markt Date: Thu Jul 1 09:35:23 2010 New Revision: 959568 URL: http://svn.apache.org/viewvc?rev=959568&view=rev Log: Improve handling for method invocation in EL, in particular the method used to identify the method to invoke. This reverts r953440 and implements a more comprehensive solution. The method identification code attempts to mimic how the Java compiler works. In a few cases it finds methods where the Java complier reports an ambiguous method. The EL spec is sufficiently vague on method selection that I don't think this will be an issue.
Added: tomcat/trunk/test/org/apache/el/TesterBeanAA.java (with props) tomcat/trunk/test/org/apache/el/TesterBeanAAA.java (with props) tomcat/trunk/test/org/apache/el/TesterBeanBBB.java (with props) tomcat/trunk/test/org/apache/el/TesterBeanC.java (with props) Modified: tomcat/trunk/java/org/apache/el/ExpressionFactoryImpl.java tomcat/trunk/java/org/apache/el/Messages.properties tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java tomcat/trunk/java/org/apache/el/parser/AstValue.java tomcat/trunk/java/org/apache/el/util/ReflectionUtil.java tomcat/trunk/test/org/apache/el/TestMethodExpressionImpl.java tomcat/trunk/test/org/apache/el/TesterBeanA.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/el/ExpressionFactoryImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/ExpressionFactoryImpl.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/el/ExpressionFactoryImpl.java (original) +++ tomcat/trunk/java/org/apache/el/ExpressionFactoryImpl.java Thu Jul 1 09:35:23 2010 @@ -51,10 +51,6 @@ public class ExpressionFactoryImpl exten public MethodExpression createMethodExpression(ELContext context, String expression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes) { - if (expectedParamTypes == null) { - throw new NullPointerException(MessageFactory - .get("error.method.nullParms")); - } ExpressionBuilder builder = new ExpressionBuilder(expression, context); return builder.createMethodExpression(expectedReturnType, expectedParamTypes); Modified: tomcat/trunk/java/org/apache/el/Messages.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/Messages.properties?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/el/Messages.properties (original) +++ tomcat/trunk/java/org/apache/el/Messages.properties Thu Jul 1 09:35:23 2010 @@ -40,6 +40,7 @@ error.syntax.set=Illegal Syntax for Set # ReflectionUtil error.method.notfound=Method not found: {0}.{1}({2}) +error.method.ambiguous=Unable to find unambiguous method: {0}.{1}({2}) error.property.notfound=Property ''{1}'' not found on {0} # ValidatingVisitor Modified: tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java (original) +++ tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java Thu Jul 1 09:35:23 2010 @@ -197,6 +197,10 @@ public final class ExpressionBuilder imp public MethodExpression createMethodExpression(Class<?> expectedReturnType, Class<?>[] expectedParamTypes) throws ELException { Node n = this.build(); + if (!n.isParametersProvided() && expectedParamTypes == null) { + throw new NullPointerException(MessageFactory + .get("error.method.nullParms")); + } if (n instanceof AstValue || n instanceof AstIdentifier) { return new MethodExpressionImpl(expression, n, this.fnMapper, this.varMapper, expectedReturnType, expectedParamTypes); Modified: tomcat/trunk/java/org/apache/el/parser/AstValue.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/parser/AstValue.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/el/parser/AstValue.java (original) +++ tomcat/trunk/java/org/apache/el/parser/AstValue.java Thu Jul 1 09:35:23 2010 @@ -18,6 +18,7 @@ package org.apache.el.parser; +import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AccessController; @@ -246,10 +247,16 @@ public final class AstValue extends Simp if (isParametersProvided()) { values = ((AstMethodParameters) this.jjtGetChild(2)).getParameters(ctx); + Class<?>[] types = getTypesFromValues(values); + m = ReflectionUtil.getMethod(t.base, t.property, types); } else { + m = ReflectionUtil.getMethod(t.base, t.property, paramTypes); values = paramValues; } - m = ReflectionUtil.getMethod(t.base, t.property, paramTypes); + if (m.isVarArgs()) { + // May need to convert values + values = toVarArgs(values, m); + } Object result = null; try { result = m.invoke(t.base, values); @@ -263,6 +270,37 @@ public final class AstValue extends Simp return result; } + private Object[] toVarArgs(Object[] src, Method m) { + int paramCount = m.getParameterTypes().length; + + Object[] dest = new Object[paramCount]; + Object[] varArgs = (Object[]) Array.newInstance( + m.getParameterTypes()[paramCount - 1].getComponentType(), + src.length - (paramCount - 1)); + System.arraycopy(src, 0, dest, 0, paramCount - 1); + System.arraycopy(src, paramCount - 1, varArgs, 0, + src.length - (paramCount - 1)); + dest[paramCount - 1] = varArgs; + return dest; + } + + private Class<?>[] getTypesFromValues(Object[] values) { + if (values == null) { + return null; + } + + Class<?> result[] = new Class<?>[values.length]; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + result[i] = null; + } else { + result[i] = values[i].getClass(); + } + } + return result; + } + + /** * @since EL 2.2 */ Modified: tomcat/trunk/java/org/apache/el/util/ReflectionUtil.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/util/ReflectionUtil.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/el/util/ReflectionUtil.java (original) +++ tomcat/trunk/java/org/apache/el/util/ReflectionUtil.java Thu Jul 1 09:35:23 2010 @@ -22,6 +22,9 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import javax.el.ELException; import javax.el.MethodNotFoundException; @@ -120,6 +123,7 @@ public class ReflectionUtil { * @return the method specified * @throws MethodNotFoundException */ + @SuppressWarnings("null") public static Method getMethod(Object base, Object property, Class<?>[] paramTypes) throws MethodNotFoundException { if (base == null || property == null) { @@ -130,16 +134,156 @@ public class ReflectionUtil { String methodName = (property instanceof String) ? (String) property : property.toString(); + + int paramCount; + if (paramTypes == null) { + paramCount = 0; + } else { + paramCount = paramTypes.length; + } - Method method = null; - try { - method = base.getClass().getMethod(methodName, paramTypes); - } catch (NoSuchMethodException nsme) { + Method[] methods = base.getClass().getMethods(); + Map<Method,Integer> candidates = new HashMap<Method,Integer>(); + + for (Method m : methods) { + if (!m.getName().equals(methodName)) { + // Method name doesn't match + continue; + } + + Class<?>[] mParamTypes = m.getParameterTypes(); + int mParamCount; + if (mParamTypes == null) { + mParamCount = 0; + } else { + mParamCount = mParamTypes.length; + } + + // Check the number of parameters + if (!(paramCount == mParamCount || + (m.isVarArgs() && paramCount >= mParamCount))) { + // Method has wrong number of parameters + continue; + } + + // Check the parameters match + int exactMatch = 0; + boolean noMatch = false; + for (int i = 0; i < mParamCount; i++) { + // Can't be null + if (mParamTypes[i].equals(paramTypes[i])) { + exactMatch++; + } else if (i == (mParamCount - 1) && m.isVarArgs()) { + Class<?> varType = mParamTypes[i].getComponentType(); + for (int j = i; j < paramCount; j++) { + if (!varType.isAssignableFrom(paramTypes[j])) { + break; + } + // Don't treat a varArgs match as an exact match, it can + // lead to a varArgs method matching when the result + // should be ambiguous + } + } else if (!mParamTypes[i].isAssignableFrom(paramTypes[i])) { + noMatch = true; + break; + } + } + if (noMatch) { + continue; + } + + // If a method is found where every parameter matches exactly, + // return it + if (exactMatch == paramCount) { + return m; + } + + candidates.put(m, Integer.valueOf(exactMatch)); + } + + // Look for the method that has the highest number of parameters where + // the type matches exactly + int bestMatch = 0; + Method match = null; + boolean multiple = false; + for (Map.Entry<Method, Integer> entry : candidates.entrySet()) { + if (entry.getValue().intValue() > bestMatch || + match == null) { + bestMatch = entry.getValue().intValue(); + match = entry.getKey(); + multiple = false; + } else if (entry.getValue().intValue() == bestMatch) { + multiple = true; + } + } + if (multiple) { + if (bestMatch == paramCount - 1) { + // Only one parameter is not an exact match - try using the + // super class + match = resolveAmbiguousMethod(candidates.keySet(), paramTypes); + } else { + match = null; + } + + if (match == null) { + // If multiple methods have the same matching number of parameters + // the match is ambiguous so throw an exception + throw new MethodNotFoundException(MessageFactory.get( + "error.method.ambiguous", base, property, + paramString(paramTypes))); + } + } + + // Handle case where no match at all was found + if (match == null) { throw new MethodNotFoundException(MessageFactory.get( - "error.method.notfound", base, property, - paramString(paramTypes))); + "error.method.notfound", base, property, + paramString(paramTypes))); + } + + return match; + } + + @SuppressWarnings("null") + private static Method resolveAmbiguousMethod(Set<Method> candidates, + Class<?>[] paramTypes) { + // Identify which parameter isn't an exact match + Method m = candidates.iterator().next(); + + int nonMatchIndex = 0; + Class<?> nonMatchClass = null; + + for (int i = 0; i < paramTypes.length; i++) { + if (m.getParameterTypes()[i] != paramTypes[i]) { + nonMatchIndex = i; + nonMatchClass = paramTypes[i]; + break; + } } - return method; + + for (Method c : candidates) { + if (c.getParameterTypes()[nonMatchIndex] == + paramTypes[nonMatchIndex]) { + // Methods have different non-matching parameters + // Result is ambiguous + return null; + } + } + + // Can't be null + nonMatchClass = nonMatchClass.getSuperclass(); + while (nonMatchClass != null) { + for (Method c : candidates) { + if (c.getParameterTypes()[nonMatchIndex].equals( + nonMatchClass)) { + // Found a match + return c; + } + } + nonMatchClass = nonMatchClass.getSuperclass(); + } + + return null; } protected static final String paramString(Class<?>[] types) { Modified: tomcat/trunk/test/org/apache/el/TestMethodExpressionImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TestMethodExpressionImpl.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/el/TestMethodExpressionImpl.java (original) +++ tomcat/trunk/test/org/apache/el/TestMethodExpressionImpl.java Thu Jul 1 09:35:23 2010 @@ -28,10 +28,50 @@ import junit.framework.TestCase; public class TestMethodExpressionImpl extends TestCase { - public void testIsParametersProvided() { - ExpressionFactory factory = ExpressionFactory.newInstance(); - ELContext context = new ELContextImpl(); + private ExpressionFactory factory; + ELContext context; + + @Override + public void setUp() { + factory = ExpressionFactory.newInstance(); + context = new ELContextImpl(); + TesterBeanA beanA = new TesterBeanA(); + beanA.setName("A"); + context.getVariableMapper().setVariable("beanA", + factory.createValueExpression(beanA, TesterBeanA.class)); + + TesterBeanAA beanAA = new TesterBeanAA(); + beanAA.setName("AA"); + context.getVariableMapper().setVariable("beanAA", + factory.createValueExpression(beanAA, TesterBeanAA.class)); + + TesterBeanAAA beanAAA = new TesterBeanAAA(); + beanAAA.setName("AAA"); + context.getVariableMapper().setVariable("beanAAA", + factory.createValueExpression(beanAAA, TesterBeanAAA.class)); + + TesterBeanB beanB = new TesterBeanB(); + beanB.setName("B"); + context.getVariableMapper().setVariable("beanB", + factory.createValueExpression(beanB, TesterBeanB.class)); + + TesterBeanBB beanBB = new TesterBeanBB(); + beanBB.setName("BB"); + context.getVariableMapper().setVariable("beanBB", + factory.createValueExpression(beanBB, TesterBeanBB.class)); + + TesterBeanBBB beanBBB = new TesterBeanBBB(); + beanBBB.setName("BBB"); + context.getVariableMapper().setVariable("beanBBB", + factory.createValueExpression(beanBBB, TesterBeanBBB.class)); + + TesterBeanC beanC = new TesterBeanC(); + context.getVariableMapper().setVariable("beanC", + factory.createValueExpression(beanC, TesterBeanC.class)); + } + + public void testIsParametersProvided() { TesterBeanB beanB = new TesterBeanB(); beanB.setName("Tomcat"); ValueExpression var = @@ -49,14 +89,11 @@ public class TestMethodExpressionImpl ex } public void testInvoke() { - ExpressionFactory factory = ExpressionFactory.newInstance(); - ELContext context = new ELContextImpl(); - TesterBeanB beanB = new TesterBeanB(); - beanB.setName("Tomcat"); - ValueExpression var = - factory.createValueExpression(beanB, TesterBeanB.class); - context.getVariableMapper().setVariable("beanB", var); + beanB.setName("B"); + + context.getVariableMapper().setVariable("beanB", + factory.createValueExpression(beanB, TesterBeanB.class)); MethodExpression me1 = factory.createMethodExpression( context, "${beanB.getName}", String.class, new Class<?>[] {}); @@ -67,39 +104,211 @@ public class TestMethodExpressionImpl ex context, "${beanB.sayHello}", String.class, new Class<?>[] { String.class }); - assertEquals("Tomcat", me1.invoke(context, null)); - assertEquals("Hello JUnit from Tomcat", me2.invoke(context, null)); - assertEquals("Hello JUnit from Tomcat", + assertEquals("B", me1.invoke(context, null)); + assertEquals("Hello JUnit from B", me2.invoke(context, null)); + assertEquals("Hello JUnit from B", me2.invoke(context, new Object[] { "JUnit2" })); - assertEquals("Hello JUnit2 from Tomcat", + assertEquals("Hello JUnit2 from B", me3.invoke(context, new Object[] { "JUnit2" })); - assertEquals("Hello JUnit from Tomcat", + assertEquals("Hello JUnit from B", me2.invoke(context, new Object[] { null })); - assertEquals("Hello null from Tomcat", + assertEquals("Hello null from B", me3.invoke(context, new Object[] { null })); } public void testInvokeWithSuper() { - ExpressionFactory factory = ExpressionFactory.newInstance(); - ELContext context = new ELContextImpl(); - - TesterBeanA beanA = new TesterBeanA(); - ValueExpression varA = - factory.createValueExpression(beanA, TesterBeanA.class); - context.getVariableMapper().setVariable("beanA", varA); - - TesterBeanBB beanC = new TesterBeanBB(); - beanC.setName("Tomcat"); - ValueExpression varC = - factory.createValueExpression(beanC, TesterBeanBB.class); - context.getVariableMapper().setVariable("beanC", varC); - - MethodExpression me1 = factory.createMethodExpression(context, - "${beanA.setBean(beanC)}", null , + MethodExpression me = factory.createMethodExpression(context, + "${beanA.setBean(beanBB)}", null , new Class<?>[] { TesterBeanB.class }); - - me1.invoke(context, null); - - assertEquals(beanA.getBean(), beanC); + me.invoke(context, null); + ValueExpression ve = factory.createValueExpression(context, + "${beanA.bean.name}", String.class); + Object r = ve.getValue(context); + assertEquals("BB", r); + } + + public void testInvokeWithSuperABNoReturnTypeNoParamTypes() { + MethodExpression me2 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanB)}", null , null); + Object r2 = me2.invoke(context, null); + assertEquals("AB: Hello A from B", r2.toString()); + } + + public void testInvokeWithSuperABReturnTypeNoParamTypes() { + MethodExpression me3 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanB)}", String.class , null); + Object r3 = me3.invoke(context, null); + assertEquals("AB: Hello A from B", r3.toString()); + } + + public void testInvokeWithSuperABNoReturnTypeParamTypes() { + MethodExpression me4 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanB)}", null , + new Class<?>[] {TesterBeanA.class, TesterBeanB.class}); + Object r4 = me4.invoke(context, null); + assertEquals("AB: Hello A from B", r4.toString()); + } + + public void testInvokeWithSuperABReturnTypeParamTypes() { + MethodExpression me5 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanB)}", String.class , + new Class<?>[] {TesterBeanA.class, TesterBeanB.class}); + Object r5 = me5.invoke(context, null); + assertEquals("AB: Hello A from B", r5.toString()); + } + + public void testInvokeWithSuperABB() { + MethodExpression me6 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanBB)}", null , null); + Object r6 = me6.invoke(context, null); + assertEquals("ABB: Hello A from BB", r6.toString()); + } + + public void testInvokeWithSuperABBB() { + MethodExpression me7 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanBBB)}", null , null); + Object r7 = me7.invoke(context, null); + assertEquals("ABB: Hello A from BBB", r7.toString()); + } + + public void testInvokeWithSuperAAB() { + MethodExpression me8 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanB)}", null , null); + Object r8 = me8.invoke(context, null); + assertEquals("AAB: Hello AA from B", r8.toString()); + } + + public void testInvokeWithSuperAABB() { + MethodExpression me9 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanBB)}", null , null); + Exception e = null; + try { + me9.invoke(context, null); + } catch (Exception e1) { + e = e1; + } + // Expected to fail + assertNotNull(e); + } + + public void testInvokeWithSuperAABBB() { + // The Java compiler reports this as ambiguous. Using the parameter that + // matches exactly seems reasonable to limit the scope of the method + // search so the EL will find a match. + MethodExpression me10 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanBBB)}", null , null); + Object r10 = me10.invoke(context, null); + assertEquals("AAB: Hello AA from BBB", r10.toString()); + } + + public void testInvokeWithSuperAAAB() { + MethodExpression me11 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanB)}", null , null); + Object r11 = me11.invoke(context, null); + assertEquals("AAB: Hello AAA from B", r11.toString()); + } + + public void testInvokeWithSuperAAABB() { + // The Java compiler reports this as ambiguous. Using the parameter that + // matches exactly seems reasonable to limit the scope of the method + // search so the EL will find a match. + MethodExpression me12 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanBB)}", null , null); + Object r12 = me12.invoke(context, null); + assertEquals("ABB: Hello AAA from BB", r12.toString()); + } + + public void testInvokeWithSuperAAABBB() { + MethodExpression me13 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanBBB)}", null , null); + Exception e = null; + try { + me13.invoke(context, null); + } catch (Exception e1) { + e = e1; + } + // Expected to fail + assertNotNull(e); + } + + public void testInvokeWithVarArgsAB() throws Exception { + MethodExpression me1 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanB,beanB)}", null , null); + Exception e = null; + try { + me1.invoke(context, null); + } catch (Exception e1) { + e = e1; + } + // Expected to fail + assertNotNull(e); + } + + public void testInvokeWithVarArgsABB() throws Exception { + MethodExpression me2 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanBB,beanBB)}", null , null); + Object r2 = me2.invoke(context, null); + assertEquals("ABB[]: Hello A from BB, BB", r2.toString()); + } + + public void testInvokeWithVarArgsABBB() throws Exception { + MethodExpression me3 = factory.createMethodExpression(context, + "${beanC.sayHello(beanA,beanBBB,beanBBB)}", null , null); + Object r3 = me3.invoke(context, null); + assertEquals("ABB[]: Hello A from BBB, BBB", r3.toString()); + } + + public void testInvokeWithVarArgsAAB() throws Exception { + MethodExpression me4 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanB,beanB)}", null , null); + Exception e = null; + try { + me4.invoke(context, null); + } catch (Exception e1) { + e = e1; + } + // Expected to fail + assertNotNull(e); + } + + public void testInvokeWithVarArgsAABB() throws Exception { + MethodExpression me5 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanBB,beanBB)}", null , null); + Object r5 = me5.invoke(context, null); + assertEquals("ABB[]: Hello AA from BB, BB", r5.toString()); + } + + public void testInvokeWithVarArgsAABBB() throws Exception { + MethodExpression me6 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAA,beanBBB,beanBBB)}", null , null); + Object r6 = me6.invoke(context, null); + assertEquals("ABB[]: Hello AA from BBB, BBB", r6.toString()); + } + + public void testInvokeWithVarArgsAAAB() throws Exception { + MethodExpression me7 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanB,beanB)}", null , null); + Exception e = null; + try { + me7.invoke(context, null); + } catch (Exception e1) { + e = e1; + } + // Expected to fail + assertNotNull(e); + } + + public void testInvokeWithVarArgsAAABB() throws Exception { + MethodExpression me8 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanBB,beanBB)}", null , null); + Object r8 = me8.invoke(context, null); + assertEquals("ABB[]: Hello AAA from BB, BB", r8.toString()); + } + + public void testInvokeWithVarArgsAAABBB() throws Exception { + MethodExpression me9 = factory.createMethodExpression(context, + "${beanC.sayHello(beanAAA,beanBBB,beanBBB)}", null , null); + Object r9 = me9.invoke(context, null); + assertEquals("ABB[]: Hello AAA from BBB, BBB", r9.toString()); } } Modified: tomcat/trunk/test/org/apache/el/TesterBeanA.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TesterBeanA.java?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/el/TesterBeanA.java (original) +++ tomcat/trunk/test/org/apache/el/TesterBeanA.java Thu Jul 1 09:35:23 2010 @@ -19,6 +19,7 @@ package org.apache.el; public class TesterBeanA { private TesterBeanB bean; + private String name; public TesterBeanB getBean() { return bean; @@ -27,4 +28,12 @@ public class TesterBeanA { public void setBean(TesterBeanB bean) { this.bean = bean; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } Added: tomcat/trunk/test/org/apache/el/TesterBeanAA.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TesterBeanAA.java?rev=959568&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/el/TesterBeanAA.java (added) +++ tomcat/trunk/test/org/apache/el/TesterBeanAA.java Thu Jul 1 09:35:23 2010 @@ -0,0 +1,23 @@ +/* + * 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.el; + +public class TesterBeanAA extends TesterBeanA { + // No additional implementation - just need a class that extends A for + // testing EL methods calls +} Propchange: tomcat/trunk/test/org/apache/el/TesterBeanAA.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/org/apache/el/TesterBeanAAA.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TesterBeanAAA.java?rev=959568&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/el/TesterBeanAAA.java (added) +++ tomcat/trunk/test/org/apache/el/TesterBeanAAA.java Thu Jul 1 09:35:23 2010 @@ -0,0 +1,23 @@ +/* + * 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.el; + +public class TesterBeanAAA extends TesterBeanAA { + // No additional implementation - just need a class that extends AA for + // testing EL methods calls +} Propchange: tomcat/trunk/test/org/apache/el/TesterBeanAAA.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/org/apache/el/TesterBeanBBB.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TesterBeanBBB.java?rev=959568&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/el/TesterBeanBBB.java (added) +++ tomcat/trunk/test/org/apache/el/TesterBeanBBB.java Thu Jul 1 09:35:23 2010 @@ -0,0 +1,23 @@ +/* + * 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.el; + +public class TesterBeanBBB extends TesterBeanBB { + // No additional implementation - just need a class that extends BB for + // testing EL methods calls +} Propchange: tomcat/trunk/test/org/apache/el/TesterBeanBBB.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/org/apache/el/TesterBeanC.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/el/TesterBeanC.java?rev=959568&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/el/TesterBeanC.java (added) +++ tomcat/trunk/test/org/apache/el/TesterBeanC.java Thu Jul 1 09:35:23 2010 @@ -0,0 +1,41 @@ +/* + * 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.el; + +public class TesterBeanC { + public String sayHello(TesterBeanA a, TesterBeanB b) { + return "AB: Hello " + a.getName() + " from " + b.getName(); + } + public String sayHello(TesterBeanAA a, TesterBeanB b) { + return "AAB: Hello " + a.getName() + " from " + b.getName(); + } + public String sayHello(TesterBeanA a, TesterBeanBB b) { + return "ABB: Hello " + a.getName() + " from " + b.getName(); + } + public String sayHello(TesterBeanA a, TesterBeanBB... b) { + StringBuilder result = + new StringBuilder("ABB[]: Hello " + a.getName() + " from "); + for (int i = 0; i < b.length; i++) { + if (i > 0) { + result.append(", "); + } + result.append(b[i].getName()); + } + return result.toString(); + } +} Propchange: tomcat/trunk/test/org/apache/el/TesterBeanC.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=959568&r1=959567&r2=959568&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Thu Jul 1 09:35:23 2010 @@ -94,6 +94,10 @@ Correct over zealous type checking for EL in attributes that broke the use of JSF convertors. (markt) </fix> + <fix> + Correct algorithm used to identify correct method to use when a + MethodExpressions is used in EL. (markt) + </fix> </changelog> </subsection> <subsection name="Cluster"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org