You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2007/09/17 18:57:59 UTC
svn commit: r576521 - in /velocity/engine/trunk/src:
java/org/apache/velocity/util/introspection/ test/org/apache/velocity/test/
Author: nbubna
Date: Mon Sep 17 09:57:56 2007
New Revision: 576521
URL: http://svn.apache.org/viewvc?rev=576521&view=rev
Log:
support varargs in method calls (VELOCITY-534)
Added:
velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java (with props)
velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java (with props)
Modified:
velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/MethodMap.java
velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java
Added: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java?rev=576521&view=auto
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java (added)
+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java Mon Sep 17 09:57:56 2007
@@ -0,0 +1,192 @@
+package org.apache.velocity.util.introspection;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author Nathan Bubna
+ * @version $Id: IntrospectionUtils.java 476785 2006-11-19 10:06:21Z henning $
+ */
+public class IntrospectionUtils
+{
+
+ /**
+ * Determines whether a type represented by a class object is
+ * convertible to another type represented by a class object using a
+ * method invocation conversion, treating object types of primitive
+ * types as if they were primitive types (that is, a Boolean actual
+ * parameter type matches boolean primitive formal type). This behavior
+ * is because this method is used to determine applicable methods for
+ * an actual parameter list, and primitive types are represented by
+ * their object duals in reflective method calls.
+ *
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return true if either formal type is assignable from actual type,
+ * or formal is a primitive type and actual is its corresponding object
+ * type or an object type of a primitive type that can be converted to
+ * the formal type.
+ */
+ public static boolean isMethodInvocationConvertible(Class formal,
+ Class actual,
+ boolean possibleVarArg)
+ {
+ /* if it's a null, it means the arg was null */
+ if (actual == null && !formal.isPrimitive())
+ {
+ return true;
+ }
+
+ /* Check for identity or widening reference conversion */
+ if (actual != null && formal.isAssignableFrom(actual))
+ {
+ return true;
+ }
+
+ /* Check for boxing with widening primitive conversion. Note that
+ * actual parameters are never primitives. */
+ if (formal.isPrimitive())
+ {
+ if(formal == Boolean.TYPE && actual == Boolean.class)
+ return true;
+ if(formal == Character.TYPE && actual == Character.class)
+ return true;
+ if(formal == Byte.TYPE && actual == Byte.class)
+ return true;
+ if(formal == Short.TYPE &&
+ (actual == Short.class || actual == Byte.class))
+ return true;
+ if(formal == Integer.TYPE &&
+ (actual == Integer.class || actual == Short.class ||
+ actual == Byte.class))
+ return true;
+ if(formal == Long.TYPE &&
+ (actual == Long.class || actual == Integer.class ||
+ actual == Short.class || actual == Byte.class))
+ return true;
+ if(formal == Float.TYPE &&
+ (actual == Float.class || actual == Long.class ||
+ actual == Integer.class || actual == Short.class ||
+ actual == Byte.class))
+ return true;
+ if(formal == Double.TYPE &&
+ (actual == Double.class || actual == Float.class ||
+ actual == Long.class || actual == Integer.class ||
+ actual == Short.class || actual == Byte.class))
+ return true;
+ }
+
+ /* Check for vararg conversion. */
+ if (possibleVarArg && formal.isArray())
+ {
+ if (actual.isArray())
+ {
+ actual = actual.getComponentType();
+ }
+ return isMethodInvocationConvertible(formal.getComponentType(),
+ actual, false);
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether a type represented by a class object is
+ * convertible to another type represented by a class object using a
+ * method invocation conversion, without matching object and primitive
+ * types. This method is used to determine the more specific type when
+ * comparing signatures of methods.
+ *
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return true if either formal type is assignable from actual type,
+ * or formal and actual are both primitive types and actual can be
+ * subject to widening conversion to formal.
+ */
+ public static boolean isStrictMethodInvocationConvertible(Class formal,
+ Class actual,
+ boolean possibleVarArg)
+ {
+ /* we shouldn't get a null into, but if so */
+ if (actual == null && !formal.isPrimitive())
+ {
+ return true;
+ }
+
+ /* Check for identity or widening reference conversion */
+ if(formal.isAssignableFrom(actual))
+ {
+ return true;
+ }
+
+ /* Check for widening primitive conversion. */
+ if(formal.isPrimitive())
+ {
+ if(formal == Short.TYPE && (actual == Byte.TYPE))
+ return true;
+ if(formal == Integer.TYPE &&
+ (actual == Short.TYPE || actual == Byte.TYPE))
+ return true;
+ if(formal == Long.TYPE &&
+ (actual == Integer.TYPE || actual == Short.TYPE ||
+ actual == Byte.TYPE))
+ return true;
+ if(formal == Float.TYPE &&
+ (actual == Long.TYPE || actual == Integer.TYPE ||
+ actual == Short.TYPE || actual == Byte.TYPE))
+ return true;
+ if(formal == Double.TYPE &&
+ (actual == Float.TYPE || actual == Long.TYPE ||
+ actual == Integer.TYPE || actual == Short.TYPE ||
+ actual == Byte.TYPE))
+ return true;
+ }
+
+ /* Check for vararg conversion. */
+ if (possibleVarArg && formal.isArray())
+ {
+ if (actual.isArray())
+ {
+ actual = actual.getComponentType();
+ }
+ return isStrictMethodInvocationConvertible(formal.getComponentType(),
+ actual, false);
+ }
+ return false;
+ }
+}
Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/MethodMap.java?rev=576521&r1=576520&r2=576521&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/MethodMap.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/MethodMap.java Mon Sep 17 09:57:56 2007
@@ -244,12 +244,13 @@
{
if(c1[i] != c2[i])
{
+ boolean last = (i == c1.length - 1);
c1MoreSpecific =
c1MoreSpecific ||
- isStrictMethodInvocationConvertible(c2[i], c1[i]);
+ isStrictConvertible(c2[i], c1[i], last);
c2MoreSpecific =
c2MoreSpecific ||
- isStrictMethodInvocationConvertible(c1[i], c2[i]);
+ isStrictConvertible(c1[i], c2[i], last);
}
}
@@ -318,158 +319,83 @@
{
Class[] methodArgs = method.getParameterTypes();
- if(methodArgs.length != classes.length)
+ if (methodArgs.length > classes.length)
{
- return false;
- }
-
- for(int i = 0; i < classes.length; ++i)
- {
- if(!isMethodInvocationConvertible(methodArgs[i], classes[i]))
+ // if there's just one more methodArg than class arg
+ // and the last methodArg is an array, then treat it as a vararg
+ if (methodArgs.length == classes.length + 1 &&
+ methodArgs[methodArgs.length - 1].isArray())
+ {
+ return true;
+ }
+ else
{
return false;
}
}
-
- return true;
- }
-
- /**
- * Determines whether a type represented by a class object is
- * convertible to another type represented by a class object using a
- * method invocation conversion, treating object types of primitive
- * types as if they were primitive types (that is, a Boolean actual
- * parameter type matches boolean primitive formal type). This behavior
- * is because this method is used to determine applicable methods for
- * an actual parameter list, and primitive types are represented by
- * their object duals in reflective method calls.
- *
- * @param formal the formal parameter type to which the actual
- * parameter type should be convertible
- * @param actual the actual parameter type.
- * @return true if either formal type is assignable from actual type,
- * or formal is a primitive type and actual is its corresponding object
- * type or an object type of a primitive type that can be converted to
- * the formal type.
- */
- private static boolean isMethodInvocationConvertible(Class formal,
- Class actual)
- {
- /*
- * if it's a null, it means the arg was null
- */
- if (actual == null && !formal.isPrimitive())
+ else if (methodArgs.length == classes.length)
{
- return true;
+ // this will properly match when the last methodArg
+ // is an array/varargs and the last class is the type of array
+ // (e.g. String when the method is expecting String...)
+ for(int i = 0; i < classes.length; ++i)
+ {
+ if(!isConvertible(methodArgs[i], classes[i], false))
+ {
+ // if we're on the last arg and the method expects an array
+ if (i == classes.length - 1 && methodArgs[i].isArray())
+ {
+ // check to see if the last arg is convertible
+ // to the array's component type
+ return isConvertible(methodArgs[i], classes[i], true);
+ }
+ return false;
+ }
+ }
}
-
- /*
- * Check for identity or widening reference conversion
- */
-
- if (actual != null && formal.isAssignableFrom(actual))
+ else // more arguments given than the method accepts; check for varargs
{
- return true;
- }
+ // check that the last methodArg is an array
+ Class lastarg = methodArgs[methodArgs.length - 1];
+ if (!lastarg.isArray())
+ {
+ return false;
+ }
- /*
- * Check for boxing with widening primitive conversion. Note that
- * actual parameters are never primitives.
- */
+ // check that they all match up to the last method arg
+ for (int i = 0; i < methodArgs.length - 1; ++i)
+ {
+ if (!isConvertible(methodArgs[i], classes[i], false))
+ {
+ return false;
+ }
+ }
- if (formal.isPrimitive())
- {
- if(formal == Boolean.TYPE && actual == Boolean.class)
- return true;
- if(formal == Character.TYPE && actual == Character.class)
- return true;
- if(formal == Byte.TYPE && actual == Byte.class)
- return true;
- if(formal == Short.TYPE &&
- (actual == Short.class || actual == Byte.class))
- return true;
- if(formal == Integer.TYPE &&
- (actual == Integer.class || actual == Short.class ||
- actual == Byte.class))
- return true;
- if(formal == Long.TYPE &&
- (actual == Long.class || actual == Integer.class ||
- actual == Short.class || actual == Byte.class))
- return true;
- if(formal == Float.TYPE &&
- (actual == Float.class || actual == Long.class ||
- actual == Integer.class || actual == Short.class ||
- actual == Byte.class))
- return true;
- if(formal == Double.TYPE &&
- (actual == Double.class || actual == Float.class ||
- actual == Long.class || actual == Integer.class ||
- actual == Short.class || actual == Byte.class))
- return true;
+ // check that all remaining arguments are convertible to the vararg type
+ Class vararg = lastarg.getComponentType();
+ for (int i = methodArgs.length - 1; i < classes.length; ++i)
+ {
+ if (!isConvertible(vararg, classes[i], false))
+ {
+ return false;
+ }
+ }
}
- return false;
+ return true;
}
- /**
- * Determines whether a type represented by a class object is
- * convertible to another type represented by a class object using a
- * method invocation conversion, without matching object and primitive
- * types. This method is used to determine the more specific type when
- * comparing signatures of methods.
- *
- * @param formal the formal parameter type to which the actual
- * parameter type should be convertible
- * @param actual the actual parameter type.
- * @return true if either formal type is assignable from actual type,
- * or formal and actual are both primitive types and actual can be
- * subject to widening conversion to formal.
- */
- private static boolean isStrictMethodInvocationConvertible(Class formal,
- Class actual)
+ private static boolean isConvertible(Class formal, Class actual,
+ boolean possibleVarArg)
{
- /*
- * we shouldn't get a null into, but if so
- */
- if (actual == null && !formal.isPrimitive())
- {
- return true;
- }
-
- /*
- * Check for identity or widening reference conversion
- */
-
- if(formal.isAssignableFrom(actual))
- {
- return true;
- }
-
- /*
- * Check for widening primitive conversion.
- */
+ return IntrospectionUtils.
+ isMethodInvocationConvertible(formal, actual, possibleVarArg);
+ }
- if(formal.isPrimitive())
- {
- if(formal == Short.TYPE && (actual == Byte.TYPE))
- return true;
- if(formal == Integer.TYPE &&
- (actual == Short.TYPE || actual == Byte.TYPE))
- return true;
- if(formal == Long.TYPE &&
- (actual == Integer.TYPE || actual == Short.TYPE ||
- actual == Byte.TYPE))
- return true;
- if(formal == Float.TYPE &&
- (actual == Long.TYPE || actual == Integer.TYPE ||
- actual == Short.TYPE || actual == Byte.TYPE))
- return true;
- if(formal == Double.TYPE &&
- (actual == Float.TYPE || actual == Long.TYPE ||
- actual == Integer.TYPE || actual == Short.TYPE ||
- actual == Byte.TYPE))
- return true;
- }
- return false;
+ private static boolean isStrictConvertible(Class formal, Class actual,
+ boolean possibleVarArg)
+ {
+ return IntrospectionUtils.
+ isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
}
}
Modified: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java?rev=576521&r1=576520&r2=576521&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java Mon Sep 17 09:57:56 2007
@@ -19,6 +19,7 @@
* under the License.
*/
+import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Enumeration;
@@ -167,7 +168,8 @@
{
return new VelMethodImpl(m);
}
- else if (obj.getClass().isArray())
+ // if it's an array, check if we support this method automagically
+ if (obj.getClass().isArray())
{
// only return *supported* array methods
if (VelArrayMethod.supports(methodName, args))
@@ -283,6 +285,7 @@
public static class VelMethodImpl implements VelMethod
{
final Method method;
+ Boolean isVarArg;
/**
* @param m
@@ -300,10 +303,102 @@
/**
* @see VelMethod#invoke(java.lang.Object, java.lang.Object[])
*/
- public Object invoke(Object o, Object[] params)
+ public Object invoke(Object o, Object[] actual)
throws Exception
{
- return method.invoke(o, params);
+ if (isVarArg())
+ {
+ Class[] formal = method.getParameterTypes();
+ int index = formal.length - 1;
+ Class type = formal[index].getComponentType();
+ if (actual.length >= index)
+ {
+ actual = handleVarArg(type, index, actual);
+ }
+ }
+ return method.invoke(o, actual);
+ }
+
+ /**
+ * @returns true if this method can accept a variable number of arguments
+ */
+ public boolean isVarArg()
+ {
+ if (isVarArg == null)
+ {
+ Class[] formal = method.getParameterTypes();
+ if (formal == null || formal.length == 0)
+ {
+ this.isVarArg = Boolean.FALSE;
+ }
+ else
+ {
+ Class last = formal[formal.length - 1];
+ // if the last arg is an array, then
+ // we consider this a varargs method
+ this.isVarArg = Boolean.valueOf(last.isArray());
+ }
+ }
+ return isVarArg.booleanValue();
+ }
+
+ /**
+ * @param type The vararg class type (aka component type
+ * of the expected array arg)
+ * @param index The index of the vararg in the method declaration
+ * (This will always be one less than the number of
+ * expected arguments.)
+ * @param actual The actual parameters being passed to this method
+ * @returns The actual parameters adjusted for the varargs in order
+ * to fit the method declaration.
+ */
+ private Object[] handleVarArg(final Class type,
+ final int index,
+ Object[] actual)
+ {
+ // if no values are being passed into the vararg
+ if (actual.length == index)
+ {
+ // create an empty array of the expected type
+ actual = new Object[] { Array.newInstance(type, 0) };
+ }
+ // if one value is being passed into the vararg
+ else if (actual.length == index + 1)
+ {
+ // make sure the last arg is an array of the expected type
+ if (IntrospectionUtils.isMethodInvocationConvertible(type,
+ actual[index].getClass(),
+ false))
+ {
+ // create a 1-length array to hold and replace the last param
+ Object lastActual = Array.newInstance(type, 1);
+ Array.set(lastActual, 0, actual[index]);
+ actual[index] = lastActual;
+ }
+ }
+ // if multiple values are being passed into the vararg
+ else if (actual.length > index + 1)
+ {
+ // put the last and extra actual in an array of the expected type
+ int size = actual.length - index;
+ Object lastActual = Array.newInstance(type, size);
+ for (int i = 0; i < size; i++)
+ {
+ Array.set(lastActual, i, actual[index + i]);
+ }
+
+ // put all into a new actual array of the appropriate size
+ Object[] newActual = new Object[index + 1];
+ for (int i = 0; i < index; i++)
+ {
+ newActual[i] = actual[i];
+ }
+ newActual[index] = lastActual;
+
+ // replace the old actual array
+ actual = newActual;
+ }
+ return actual;
}
/**
Added: velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java?rev=576521&view=auto
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java (added)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java Mon Sep 17 09:57:56 2007
@@ -0,0 +1,213 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.log.SystemLogChute;
+
+/**
+ * Used to check that vararg method calls on references work properly
+ */
+public class VarargMethodsTestCase extends TestCase
+{
+ private VelocityEngine engine;
+ private VelocityContext context;
+
+ public VarargMethodsTestCase(final String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(VarargMethodsTestCase.class);
+ }
+
+ public void setUp() throws Exception
+ {
+ engine = new VelocityEngine();
+
+ // make the engine's log output go to the test-report
+ SystemLogChute log = new SystemLogChute();
+ log.setEnabledLevel(SystemLogChute.INFO_ID);
+ log.setSystemErrLevel(SystemLogChute.WARN_ID);
+ engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, log);
+
+ context = new VelocityContext();
+ context.put("nice", new NiceTool());
+ context.put("nasty", new NastyTool());
+ context.put("objects", new Object[] { this, Test.class });
+ context.put("strings", new String[] { "one", "two" });
+ context.put("doubles", new double[] { 1.5, 2.5 });
+ context.put("float", Float.valueOf(1f));
+ context.put("ints", new int[] { 1, 2 });
+ }
+
+ public void tearDown()
+ {
+ engine = null;
+ context = null;
+ }
+
+ public void testStrings()
+ {
+ assertEvalEquals("onetwo", "$nice.var($strings)");
+ assertEvalEquals("onetwo", "$nice.var('one','two')");
+ assertEvalEquals("one", "$nice.var('one')");
+ assertEvalEquals("", "$nice.var()");
+ }
+
+ public void testDoubles()
+ {
+ assertEvalEquals("4.0", "$nice.add($doubles)");
+ assertEvalEquals("3.0", "$nice.add(1,2)");
+ assertEvalEquals("1.0", "$nice.add(1)");
+ assertEvalEquals("0.0", "$nice.add()");
+ }
+
+ public void testFloatToDoubleVarArg()
+ {
+ assertEvalEquals("1.0", "$nice.add($float)");
+ }
+
+ public void testStringVsStrings()
+ {
+ assertEvalEquals("onlyone", "$nasty.var('one')");
+ assertEvalEquals("onlynull", "$nasty.var($null)");
+ assertEvalEquals("", "$nasty.var()");
+ }
+
+ public void testIntVsDoubles()
+ {
+ assertEvalEquals("1", "$nasty.add(1)");
+ assertEvalEquals("1.0", "$nasty.add(1.0)");
+ assertEvalEquals("3.0", "$nasty.add(1.0,2)");
+ }
+
+ public void testInts()
+ {
+ assertEvalEquals("3", "$nasty.add($ints)");
+ assertEvalEquals("3", "$nasty.add(1,2)");
+ assertEvalEquals("1", "$nasty.add(1)");
+ // add(int[]) wins because it is "more specific"
+ assertEvalEquals("0", "$nasty.add()");
+ }
+
+ public void testStringsVsObjectsAKASubclassVararg()
+ {
+ assertEvalEquals("objects", "$nice.test($objects)");
+ assertEvalEquals("objects", "$nice.test($nice,$nasty,$ints)");
+ assertEvalEquals("strings", "$nice.test('foo')");
+ }
+
+
+ protected void assertEvalEquals(String expected, String template)
+ {
+ try
+ {
+ assertEquals(expected, evaluate(template));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String evaluate(String template) throws Exception
+ {
+ StringWriter writer = new StringWriter();
+ // use template as its own name, since our templates are short
+ engine.evaluate(context, writer, template, template);
+ return writer.toString();
+ }
+
+
+
+ public static class NiceTool
+ {
+ public String var(String[] ss)
+ {
+ StringBuffer out = new StringBuffer();
+ for (int i=0; i < ss.length; i++)
+ {
+ out.append(ss[i]);
+ }
+ return out.toString();
+ }
+
+ public double add(double[] dd)
+ {
+ double total = 0;
+ for (int i=0; i < dd.length; i++)
+ {
+ total += dd[i];
+ }
+ return total;
+ }
+
+ public String test(Object[] oo)
+ {
+ return "objects";
+ }
+
+ public String test(String[] oo)
+ {
+ return "strings";
+ }
+
+ }
+
+ public static class NastyTool extends NiceTool
+ {
+ public String var(String s)
+ {
+ return "only"+s;
+ }
+
+ public int add(int[] ii)
+ {
+ int total = 0;
+ for (int i=0; i < ii.length; i++)
+ {
+ total += ii[i];
+ }
+ return total;
+ }
+
+ public int add(int i)
+ {
+ return i;
+ }
+
+ }
+
+}
+
+
Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/VarargMethodsTestCase.java
------------------------------------------------------------------------------
svn:mime-type = text/plain