You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2017/04/08 13:20:55 UTC
svn commit: r1790680 - in /velocity/engine/trunk: ./
velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/
velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/
Author: cbrisson
Date: Sat Apr 8 13:20:55 2017
New Revision: 1790680
URL: http://svn.apache.org/viewvc?rev=1790680&view=rev
Log:
[engine] Full review of method disambiguation
Modified:
velocity/engine/trunk/pom.xml
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java
Modified: velocity/engine/trunk/pom.xml
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/pom.xml?rev=1790680&r1=1790679&r2=1790680&view=diff
==============================================================================
--- velocity/engine/trunk/pom.xml (original)
+++ velocity/engine/trunk/pom.xml Sat Apr 8 13:20:55 2017
@@ -83,8 +83,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
- <debug>false</debug>
- <optimize>true</optimize>
+ <debug>true</debug>
+ <optimize>false</optimize>
<showDeprecation>true</showDeprecation>
<showWarning>true</showWarning>
<source>${maven.compiler.source}</source>
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java?rev=1790680&r1=1790679&r2=1790680&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java Sat Apr 8 13:20:55 2017
@@ -216,10 +216,10 @@ public class IntrospectionUtils
Class actual,
boolean possibleVarArg)
{
- /* we shouldn't get a null into, but if so */
- if (actual == null && !formal.isPrimitive())
+ /* Check for nullity */
+ if (actual == null)
{
- return true;
+ return !formal.isPrimitive();
}
/* Check for identity or widening reference conversion */
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java?rev=1790680&r1=1790679&r2=1790680&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java Sat Apr 8 13:20:55 2017
@@ -19,10 +19,14 @@ package org.apache.velocity.util.introsp
* under the License.
*/
+import org.apache.velocity.exception.VelocityException;
+
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Arrays;
+import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -38,9 +42,17 @@ import java.util.concurrent.ConcurrentHa
*/
public class MethodMap
{
- private static final int MORE_SPECIFIC = 0;
- private static final int LESS_SPECIFIC = 1;
- private static final int INCOMPARABLE = 2;
+ /* Constants for specificity */
+ private static final int INCOMPARABLE = 0;
+ private static final int MORE_SPECIFIC = 1;
+ private static final int EQUIVALENT = 2;
+ private static final int LESS_SPECIFIC = 3;
+
+ /* Constants for applicability */
+ private static final int NOT_CONVERTIBLE = 0;
+ private static final int EXPLICITLY_CONVERTIBLE = 1;
+ private static final int IMPLCITLY_CONVERTIBLE = 2;
+ private static final int STRICTLY_CONVERTIBLE = 3;
ConversionHandler conversionHandler;
@@ -155,12 +167,43 @@ public class MethodMap
return getBestMatch(methodList, classes);
}
+ private class Match
+ {
+ /* target method */
+ Method method;
+
+ /* cache arguments classes array */
+ Class[] methodTypes;
+
+ /* specificity: how does the best match compare to provided arguments
+ * one one LESS_SPECIFIC, MORE_SPECIFIC or INCOMPARABLE */
+ int specificity;
+
+ /* applicability which conversion level is needed against provided arguments
+ * one of STRICTLY_CONVERTIBLE, IMPLICITLY_CONVERTIBLE and EXPLICITLY_CONVERTIBLE_ */
+ int applicability;
+
+ Match(Method method, int applicability, Class[] unboxedArgs)
+ {
+ this.method = method;
+ this.applicability = applicability;
+ this.methodTypes = method.getParameterTypes();
+ this.specificity = compare(methodTypes, unboxedArgs);
+ }
+ }
+
+ private static boolean onlyNullOrObjects(Class[] args)
+ {
+ for (Class cls : args)
+ {
+ if (cls != null && cls != Object.class) return false;
+ }
+ return true;
+ }
+
private Method getBestMatch(List<Method> methods, Class[] args)
{
- List equivalentMatches = null;
- Method bestMatch = null;
- Class[] bestMatchTypes = null;
- int bestMatchComp = INCOMPARABLE; /* how does the best match compare to provided type */
+ List<Match> bestMatches = new LinkedList<Match>();
Class[] unboxedArgs = new Class[args.length];
for (int i = 0; i < args.length; ++i)
{
@@ -168,128 +211,79 @@ public class MethodMap
}
for (Method method : methods)
{
- if (isApplicable(method, args))
+ int applicability = getApplicability(method, args);
+ if (applicability > NOT_CONVERTIBLE)
{
- if (bestMatch == null)
+ Match match = new Match(method, applicability, unboxedArgs);
+ if (bestMatches.size() == 0)
{
- bestMatch = method;
- bestMatchTypes = method.getParameterTypes();
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- } else
- {
- Class[] methodTypes = method.getParameterTypes();
- switch (compare(methodTypes, bestMatchTypes))
- {
- case MORE_SPECIFIC:
- /* do not retain method if it's more specific than (or incomparable to) provided (unboxed) arguments
- * while best batch is less specific
- */
- if (bestMatchComp == LESS_SPECIFIC && compare(methodTypes, unboxedArgs) != LESS_SPECIFIC)
- {
- break;
- }
- if (equivalentMatches == null)
- {
- bestMatch = method;
- bestMatchTypes = methodTypes;
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- } else
+ bestMatches.add(match);
+ }
+ else
+ {
+ /* filter existing matches */
+ boolean keepMethod = true;
+ for (ListIterator<Match> it = bestMatches.listIterator(); keepMethod && it.hasNext();)
+ {
+ Match best = it.next();
+ /* do not retain match if it's more specific than (or incomparable to) provided (unboxed) arguments
+ * while one of the best matches is less specific
+ */
+ if (best.specificity == LESS_SPECIFIC && match.specificity < EQUIVALENT) /* != LESS_SPECIFIC && != EQUIVALENT */
+ {
+ keepMethod = false;
+ }
+ /* drop considered best match if match is less specific than (unboxed) provided args while
+ * the considered best match is more specific or incomparable
+ */
+ else if (match.specificity == LESS_SPECIFIC && best.specificity < EQUIVALENT) /* != LESS_SPECIFIC && != EQUIVALENT */
+ {
+ it.remove();
+ }
+ /* compare methods between them */
+ else
+ {
+ /* but only if some provided args are non null and not Object */
+ if (!onlyNullOrObjects(args))
{
- /* have to beat all other ambiguous ones... */
- int ambiguities = equivalentMatches.size();
- for (int a = 0; a < ambiguities; a++)
+ switch (compare(match.methodTypes, best.methodTypes))
{
- Method other = (Method) equivalentMatches.get(a);
- switch (compare(methodTypes, other.getParameterTypes()))
- {
- case MORE_SPECIFIC:
- /* ...and thus replace them all...
- * but do not retain method if it's more specific than (or incomparable to) provided (unboxed) arguments
- * while best batch is less specific
- */
- if (bestMatchComp == LESS_SPECIFIC && compare(methodTypes, unboxedArgs) != LESS_SPECIFIC)
- {
- break;
- }
- bestMatch = method;
- bestMatchTypes = methodTypes;
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- equivalentMatches = null;
- ambiguities = 0;
- break;
-
- case INCOMPARABLE:
- /* ...join them...*/
- equivalentMatches.add(method);
- break;
-
- case LESS_SPECIFIC:
- /* retain it anyway if less specific than (unboxed) provided args while
- * bestmatch is more specific
- */
- if (bestMatchComp == MORE_SPECIFIC && compare(methodTypes, unboxedArgs) == LESS_SPECIFIC)
- {
- bestMatch = method;
- bestMatchTypes = methodTypes;
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- equivalentMatches = null;
- ambiguities = 0;
- }
- break;
- }
+ case LESS_SPECIFIC:
+ keepMethod = false;
+ break;
+ case MORE_SPECIFIC:
+ it.remove();
+ break;
+ case EQUIVALENT:
+ case INCOMPARABLE:
+ /* compare applicability */
+ if (best.applicability > match.applicability)
+ {
+ keepMethod = false;
+ } else if (best.applicability < match.applicability)
+ {
+ it.remove();
+ }
+ /* otherwise it's an equivalent match */
+ break;
}
}
- break;
-
- case INCOMPARABLE:
- /* do not retain method if it's more specific than (or incomparable to) provided (unboxed) arguments
- * while best batch is less specific
- */
- if (bestMatchComp == LESS_SPECIFIC && compare(methodTypes, unboxedArgs) != LESS_SPECIFIC)
- {
- break;
- }
- /* retain it anyway if less specific than (unboxed) provided args while
- * bestmatch is more specific or incomparable
- */
- if (bestMatchComp != LESS_SPECIFIC && compare(methodTypes, unboxedArgs) == LESS_SPECIFIC)
- {
- bestMatch = method;
- bestMatchTypes = methodTypes;
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- equivalentMatches = null;
- break;
- }
- if (equivalentMatches == null)
- {
- equivalentMatches = new ArrayList(bestMatchTypes.length);
- }
- equivalentMatches.add(method);
- break;
-
- case LESS_SPECIFIC:
- /* retain it anyway if less specific than (unboxed) provided args while
- * bestmatch is more specific or incomparable
- */
- if (bestMatchComp != LESS_SPECIFIC && compare(methodTypes, unboxedArgs) == LESS_SPECIFIC)
- {
- bestMatch = method;
- bestMatchTypes = methodTypes;
- bestMatchComp = compare(bestMatchTypes, unboxedArgs);
- equivalentMatches = null;
- }
- break;
+ }
+ }
+ if (keepMethod)
+ {
+ bestMatches.add(match);
}
}
}
}
- if (equivalentMatches != null)
+ switch (bestMatches.size())
{
- System.out.println("ambiguous: "+equivalentMatches);
- throw new AmbiguousException();
+ case 0: return null;
+ case 1: return bestMatches.get(0).method;
+ default: throw new AmbiguousException();
}
- return bestMatch;
}
/**
@@ -317,104 +311,172 @@ public class MethodMap
{
boolean c1MoreSpecific = false;
boolean c2MoreSpecific = false;
+ boolean fixedLengths = false;
// compare lengths to handle comparisons where the size of the arrays
// doesn't match, but the methods are both applicable due to the fact
// that one is a varargs method
if (c1.length > c2.length)
{
- return MORE_SPECIFIC;
- }
- if (c2.length > c1.length)
- {
- return LESS_SPECIFIC;
- }
-
- // ok, move on and compare those of equal lengths
- for(int i = 0; i < c1.length; ++i)
- {
- if(c1[i] != c2[i] && c1[i] != null && c2[i] != null)
+ int l2 = c2.length;
+ if (l2 == 0)
{
- boolean last = (i == c1.length - 1);
- c1MoreSpecific =
- c1MoreSpecific ||
- isStrictConvertible(c2[i], c1[i], last) ||
- c2[i] == Object.class;//Object is always least-specific
- c2MoreSpecific =
- c2MoreSpecific ||
- isStrictConvertible(c1[i], c2[i], last) ||
- c1[i] == Object.class;//Object is always least-specific
+ return MORE_SPECIFIC;
}
+ c2 = Arrays.copyOf(c2, c1.length);
+ Class itemClass = c2[l2 - 1].getComponentType();
+ /* if item class is null, then it implies the vaarg is #1
+ * (and receives an empty array)
+ */
+ if (itemClass == null)
+ {
+ /* by construct, we have c1.length = l2 + 1 */
+ c2[c1.length - 1] = c1[c1.length - 1];
+ }
+ else
+ {
+ for (int i = l2 - 1; i < c1.length; ++i)
+ {
+ /* also overwrite the vaargs itself */
+ c2[i] = itemClass;
+ }
+ }
+ fixedLengths = true;
}
-
- /* check for conversions */
- if (!c1MoreSpecific && !c2MoreSpecific)
+ else if (c2.length > c1.length)
{
- for(int i = 0; i < c1.length; ++i)
+ int l1 = c1.length;
+ if (l1 == 0)
+ {
+ return LESS_SPECIFIC;
+ }
+ c1 = Arrays.copyOf(c1, c2.length);
+ Class itemClass = c1[l1 - 1].getComponentType();
+ /* if item class is null, then it implies the vaarg is #2
+ * (and receives an empty array)
+ */
+ if (itemClass == null)
{
- boolean last = (i == c1.length - 1);
- if (c1[i] != c2[i] && c1[i] != null && c2[i] != null)
+ /* by construct, we have c2.length = l1 + 1 */
+ c1[c2.length - 1] = c2[c2.length - 1];
+ }
+ else
+ {
+ for (int i = l1 - 1; i < c2.length; ++i)
{
- c1MoreSpecific = c1MoreSpecific || isConvertible(c2[i], c1[i], last);
- c2MoreSpecific = c2MoreSpecific || isConvertible(c1[i], c2[i], last);
+ /* also overwrite the vaargs itself */
+ c1[i] = itemClass;
}
}
+ fixedLengths = true;
}
- if(c1MoreSpecific)
+ /* ok, move on and compare those of equal lengths */
+ int fromC1toC2 = STRICTLY_CONVERTIBLE;
+ int fromC2toC1 = STRICTLY_CONVERTIBLE;
+ for(int i = 0; i < c1.length; ++i)
{
- if(c2MoreSpecific)
+ boolean last = !fixedLengths && (i == c1.length - 1);
+ if (c1[i] != c2[i])
{
- /*
- * If one method accepts varargs and the other does not,
- * call the non-vararg one more specific.
- */
- boolean last1Array = c1[c1.length - 1].isArray();
- boolean last2Array = c2[c2.length - 1].isArray();
- if (last1Array && !last2Array)
+ if (c1[i] == null)
{
- return LESS_SPECIFIC;
+ fromC2toC1 = NOT_CONVERTIBLE;
+ if (c2[i].isPrimitive())
+ {
+ fromC1toC2 = NOT_CONVERTIBLE;
+ }
}
- if (!last1Array && last2Array)
+ else if (c2[i] == null)
{
- return MORE_SPECIFIC;
+ fromC1toC2 = NOT_CONVERTIBLE;
+ if (c1[i].isPrimitive())
+ {
+ fromC2toC1 = NOT_CONVERTIBLE;
+ }
+ }
+ else
+ {
+ switch (fromC1toC2)
+ {
+ case STRICTLY_CONVERTIBLE:
+ if (isStrictConvertible(c2[i], c1[i], last)) break;
+ fromC1toC2 = IMPLCITLY_CONVERTIBLE;
+ case IMPLCITLY_CONVERTIBLE:
+ if (isConvertible(c2[i], c1[i], last)) break;
+ fromC1toC2 = EXPLICITLY_CONVERTIBLE;
+ case EXPLICITLY_CONVERTIBLE:
+ if (isExplicitlyConvertible(c2[i], c1[i], last)) break;
+ fromC1toC2 = NOT_CONVERTIBLE;
+ }
+ switch (fromC2toC1)
+ {
+ case STRICTLY_CONVERTIBLE:
+ if (isStrictConvertible(c1[i], c2[i], last)) break;
+ fromC2toC1 = IMPLCITLY_CONVERTIBLE;
+ case IMPLCITLY_CONVERTIBLE:
+ if (isConvertible(c1[i], c2[i], last)) break;
+ fromC2toC1 = EXPLICITLY_CONVERTIBLE;
+ case EXPLICITLY_CONVERTIBLE:
+ if (isExplicitlyConvertible(c1[i], c2[i], last)) break;
+ fromC2toC1 = NOT_CONVERTIBLE;
+ }
}
-
- /*
- * Incomparable due to cross-assignable arguments (i.e.
- * foo(String, Object) vs. foo(Object, String))
- */
- return INCOMPARABLE;
}
+ }
- return MORE_SPECIFIC;
+ if (fromC1toC2 == NOT_CONVERTIBLE && fromC2toC1 == NOT_CONVERTIBLE)
+ {
+ /*
+ * Incomparable due to cross-assignable arguments (i.e.
+ * foo(String, Foo) vs. foo(Foo, String))
+ */
+ return INCOMPARABLE;
}
- if(c2MoreSpecific)
+ if (fromC1toC2 > fromC2toC1)
+ {
+ return MORE_SPECIFIC;
+ }
+ else if (fromC2toC1 > fromC1toC2)
{
return LESS_SPECIFIC;
}
-
- /*
- * Incomparable due to non-related arguments (i.e.
- * foo(Runnable) vs. foo(Serializable))
- */
-
- return INCOMPARABLE;
+ else
+ {
+ /*
+ * If one method accepts varargs and the other does not,
+ * call the non-vararg one more specific.
+ */
+ boolean last1Array = !fixedLengths && c1[c1.length - 1].isArray();
+ boolean last2Array = !fixedLengths && c2[c2.length - 1].isArray();
+ if (last1Array && !last2Array)
+ {
+ return LESS_SPECIFIC;
+ }
+ if (!last1Array && last2Array)
+ {
+ return MORE_SPECIFIC;
+ }
+ }
+ return EQUIVALENT;
}
/**
- * Returns true if the supplied method is applicable to actual
- * argument types.
+ * Returns the applicability of the supplied method against actual argument types.
*
* @param method method that will be called
* @param classes arguments to method
- * @return true if method is applicable to arguments
+ * @return the level of applicability:
+ * 0 = not applicable
+ * 1 = explicitly applicable (i.e. using stock or custom conversion handlers)
+ * 2 = implicitly applicable (i.e. using JAva implicit boxing/unboxing and primitive types widening)
+ * 3 = strictly applicable
*/
- private boolean isApplicable(Method method, Class[] classes)
+ private int getApplicability(Method method, Class[] classes)
{
Class[] methodArgs = method.getParameterTypes();
-
+ int ret = STRICTLY_CONVERTIBLE;
if (methodArgs.length > classes.length)
{
// if there's just one more methodArg than class arg
@@ -425,17 +487,27 @@ public class MethodMap
// all the args preceding the vararg must match
for (int i = 0; i < classes.length; i++)
{
- if (!isConvertible(methodArgs[i], classes[i], false) &&
- !isExplicitlyConvertible(methodArgs[i], classes[i], false))
+ if (!isStrictConvertible(methodArgs[i], classes[i], false))
{
- return false;
+ if (isConvertible(methodArgs[i], classes[i], false))
+ {
+ ret = Math.min(ret, IMPLCITLY_CONVERTIBLE);
+ }
+ else if (isExplicitlyConvertible(methodArgs[i], classes[i], false))
+ {
+ ret = Math.min(ret, EXPLICITLY_CONVERTIBLE);
+ }
+ else
+ {
+ return NOT_CONVERTIBLE;
+ }
}
}
- return true;
+ return ret;
}
else
{
- return false;
+ return NOT_CONVERTIBLE;
}
}
else if (methodArgs.length == classes.length)
@@ -445,21 +517,23 @@ public class MethodMap
// (e.g. String when the method is expecting String...)
for(int i = 0; i < classes.length; ++i)
{
- if(!isConvertible(methodArgs[i], classes[i], false) &&
- !isExplicitlyConvertible(methodArgs[i], classes[i], false))
+ if (!isStrictConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
{
- // if we're on the last arg and the method expects an array
- if (i == classes.length - 1 && methodArgs[i].isArray())
+ if (isConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
+ {
+ ret = Math.min(ret, IMPLCITLY_CONVERTIBLE);
+ }
+ else if (isExplicitlyConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
+ {
+ ret = Math.min(ret, EXPLICITLY_CONVERTIBLE);
+ }
+ else
{
- // check to see if the last arg is convertible
- // to the array's component type
- return isConvertible(methodArgs[i], classes[i], true) ||
- isExplicitlyConvertible(methodArgs[i], classes[i], true);
+ return NOT_CONVERTIBLE;
}
- return false;
}
}
- return true;
+ return ret;
}
else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
{
@@ -467,15 +541,26 @@ public class MethodMap
Class lastarg = methodArgs[methodArgs.length - 1];
if (!lastarg.isArray())
{
- return false;
+ return NOT_CONVERTIBLE;
}
- // check that they all match up to the last method arg
+ // check that they all match up to the last method arg component type
for (int i = 0; i < methodArgs.length - 1; ++i)
{
- if (!isConvertible(methodArgs[i], classes[i], false))
+ if (!isStrictConvertible(methodArgs[i], classes[i], false))
{
- return false;
+ if (isConvertible(methodArgs[i], classes[i], false))
+ {
+ ret = Math.min(ret, IMPLCITLY_CONVERTIBLE);
+ }
+ else if (isExplicitlyConvertible(methodArgs[i], classes[i], false))
+ {
+ ret = Math.min(ret, EXPLICITLY_CONVERTIBLE);
+ }
+ else
+ {
+ return NOT_CONVERTIBLE;
+ }
}
}
@@ -483,14 +568,25 @@ public class MethodMap
Class vararg = lastarg.getComponentType();
for (int i = methodArgs.length - 1; i < classes.length; ++i)
{
- if (!isConvertible(vararg, classes[i], false))
+ if (!isStrictConvertible(vararg, classes[i], false))
{
- return false;
+ if (isConvertible(vararg, classes[i], false))
+ {
+ ret = Math.min(ret, IMPLCITLY_CONVERTIBLE);
+ }
+ else if (isExplicitlyConvertible(vararg, classes[i], false))
+ {
+ ret = Math.min(ret, EXPLICITLY_CONVERTIBLE);
+ }
+ else
+ {
+ return NOT_CONVERTIBLE;
+ }
}
}
- return true;
+ return ret;
}
- return false;
+ return NOT_CONVERTIBLE;
}
/**
Modified: velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java?rev=1790680&r1=1790679&r2=1790680&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java Sat Apr 8 13:20:55 2017
@@ -4,6 +4,7 @@ import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.test.BaseTestCase;
import org.apache.velocity.test.misc.TestLogger;
@@ -49,50 +50,33 @@ public class UberspectImplTestCase exten
}
@Override
- public void setUp()
- throws Exception
+ protected void setUpEngine(VelocityEngine engine)
{
- Velocity.reset();
- Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, new TestLogger());
- Velocity.addProperty(RuntimeConstants.UBERSPECT_CLASSNAME,
- "org.apache.velocity.util.introspection.UberspectImpl");
- Velocity.init();
+ engine.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, new TestLogger());
+ engine.addProperty(RuntimeConstants.UBERSPECT_CLASSNAME, "org.apache.velocity.util.introspection.UberspectImpl");
}
@Override
- public void tearDown()
+ protected void setUpContext(VelocityContext context)
{
+ context.put("privateClass", new PrivateClass());
+ context.put("privateMethod", new PrivateMethod());
+ context.put("publicMethod", new PublicMethod());
+ context.put("iterable", new SomeIterable());
+ context.put("over", new OverloadedMethods());
}
public void testPrivateIterator()
throws Exception
{
- VelocityContext context = new VelocityContext();
- context.put("privateClass", new PrivateClass());
- context.put("privateMethod", new PrivateMethod());
- context.put("publicMethod", new PublicMethod());
- StringWriter writer = new StringWriter();
-
- Velocity.evaluate(context, writer, "test", "#foreach($i in $privateClass)$i#end");
- assertEquals(writer.toString(), "");
-
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "#foreach($i in $privateMethod)$i#end");
- assertEquals(writer.toString(), "");
-
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "#foreach($i in $publicMethod)$i#end");
- assertEquals(writer.toString(), "123");
+ assertEvalEquals("", "#foreach($i in $privateClass)$i#end");
+ assertEvalEquals("", "#foreach($i in $privateMethod)$i#end");
+ assertEvalEquals("123", "#foreach($i in $publicMethod)$i#end");
}
public void testIterableForeach()
{
- VelocityContext context = new VelocityContext();
- context.put("iterable", new SomeIterable());
- StringWriter writer = new StringWriter();
-
- Velocity.evaluate(context, writer, "test", "#foreach($i in $iterable)$i#end");
- assertEquals(writer.toString(), "123");
+ assertEvalEquals("123", "#foreach($i in $iterable)$i#end");
}
private class PrivateClass
@@ -132,26 +116,20 @@ public class UberspectImplTestCase exten
public String foo() { return "foo0"; }
public String foo(String arg1) { return "foo1"; }
public String foo(String arg1, String arg2) { return "foo2"; }
+
+ public String bar(Number n, int i) { return "bar1"; }
+ public String bar(Number n, String s) { return "bar2"; }
}
public void testOverloadedMethods()
{
- VelocityContext context = new VelocityContext();
- context.put("over", new OverloadedMethods());
- StringWriter writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "$over.foo()");
- assertEquals(writer.toString(), "foo0");
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "$over.foo('a')");
- assertEquals(writer.toString(), "foo1");
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "$over.foo($null)");
- assertEquals(writer.toString(), "foo1");
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "$over.foo('a', 'b')");
- assertEquals(writer.toString(), "foo2");
- writer = new StringWriter();
- Velocity.evaluate(context, writer, "test", "$over.foo('a', $null)");
- assertEquals(writer.toString(), "foo2");
+ assertEvalEquals("foo0", "$over.foo()");
+ assertEvalEquals("foo1", "$over.foo('a')");
+ assertEvalEquals("foo1", "$over.foo($null)");
+ assertEvalEquals("foo2", "$over.foo('a', 'b')");
+ assertEvalEquals("foo2", "$over.foo('a', $null)");
+ assertEvalEquals("bar1", "$over.bar(1,1)");
+ assertEvalEquals("$over.bar(1,1.1)", "$over.bar(1,1.1)"); // this one is definitely ambiguous
+ assertEvalEquals("bar2", "$over.bar(1,'1.1')");
}
}