Will this be in RC2, or was 1.3 branched? [EMAIL PROTECTED] writes:
> geirm 02/03/23 05:30:58 > > Modified: src/java/org/apache/velocity/util/introspection > MethodMap.java > Log: > Attila's patch for doing correct overloaded method selection, replacing my > wacky hack :) > > Revision Changes Path > 1.14 +468 -361 >jakarta-velocity/src/java/org/apache/velocity/util/introspection/MethodMap.java > > Index: MethodMap.java > =================================================================== > RCS file: >/home/cvs/jakarta-velocity/src/java/org/apache/velocity/util/introspection/MethodMap.java,v > retrieving revision 1.13 > retrieving revision 1.14 > diff -u -r1.13 -r1.14 > --- MethodMap.java 27 Nov 2001 00:40:46 -0000 1.13 > +++ MethodMap.java 23 Mar 2002 13:30:57 -0000 1.14 > @@ -1,376 +1,483 @@ > -package org.apache.velocity.util.introspection; > - > -/* > - * The Apache Software License, Version 1.1 > - * > - * Copyright (c) 2001 The Apache Software Foundation. All rights > - * reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * 1. Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * 2. Redistributions in binary form must reproduce the above copyright > - * notice, this list of conditions and the following disclaimer in > - * the documentation and/or other materials provided with the > - * distribution. > - * > - * 3. The end-user documentation included with the redistribution, if > - * any, must include the following acknowlegement: > - * "This product includes software developed by the > - * Apache Software Foundation (http://www.apache.org/)." > - * Alternately, this acknowlegement may appear in the software itself, > - * if and wherever such third-party acknowlegements normally appear. > - * > - * 4. The names "The Jakarta Project", "Velocity", and "Apache Software > - * Foundation" must not be used to endorse or promote products derived > - * from this software without prior written permission. For written > - * permission, please contact [EMAIL PROTECTED] > - * > - * 5. Products derived from this software may not be called "Apache" > - * nor may "Apache" appear in their names without prior written > - * permission of the Apache Group. > - * > - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED > - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES > - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE > - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR > - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF > - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND > - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, > - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT > - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > - * SUCH DAMAGE. > - * ==================================================================== > - * > - * This software consists of voluntary contributions made by many > - * individuals on behalf of the Apache Software Foundation. For more > - * information on the Apache Software Foundation, please see > - * <http://www.apache.org/>. > - */ > - > -import java.util.List; > -import java.util.ArrayList; > -import java.util.Map; > -import java.util.Hashtable; > - > -import java.lang.reflect.Method; > - > -/** > - * > - * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a> > - * @author <a href="mailto:[EMAIL PROTECTED]">Bob McWhirter</a> > - * @author <a href="mailto:[EMAIL PROTECTED]">Christoph Reck</a> > - * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a> > - * @version $Id: MethodMap.java,v 1.13 2001/11/27 00:40:46 geirm Exp $ > - */ > -public class MethodMap > +package org.apache.velocity.util.introspection; > + > +/* > + * The Apache Software License, Version 1.1 > + * > + * Copyright (c) 2001 The Apache Software Foundation. All rights > + * reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * > + * 3. The end-user documentation included with the redistribution, if > + * any, must include the following acknowlegement: > + * "This product includes software developed by the > + * Apache Software Foundation (http://www.apache.org/)." > + * Alternately, this acknowlegement may appear in the software itself, > + * if and wherever such third-party acknowlegements normally appear. > + * > + * 4. The names "The Jakarta Project", "Velocity", and "Apache Software > + * Foundation" must not be used to endorse or promote products derived > + * from this software without prior written permission. For written > + * permission, please contact [EMAIL PROTECTED] > + * > + * 5. Products derived from this software may not be called "Apache" > + * nor may "Apache" appear in their names without prior written > + * permission of the Apache Group. > + * > + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED > + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES > + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE > + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR > + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF > + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND > + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, > + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT > + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * ==================================================================== > + * > + * This software consists of voluntary contributions made by many > + * individuals on behalf of the Apache Software Foundation. For more > + * information on the Apache Software Foundation, please see > + * <http://www.apache.org/>. > + */ > + > +import java.util.Iterator; > +import java.util.List; > +import java.util.ArrayList; > +import java.util.LinkedList; > +import java.util.Set; > +import java.util.HashSet; > +import java.util.Map; > +import java.util.Hashtable; > + > +import java.lang.reflect.Method; > + > +/** > + * > + * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a> > + * @author <a href="mailto:[EMAIL PROTECTED]">Bob McWhirter</a> > + * @author <a href="mailto:[EMAIL PROTECTED]">Christoph Reck</a> > + * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a> > + * @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a> > + * @version $Id: MethodMap.java,v 1.14 2002/03/23 13:30:57 geirm Exp $ > + */ > +public class MethodMap > { > - /** > - * Keep track of all methods with the same name. > - */ > - Map methodByNameMap = new Hashtable(); > - > - /** > - * Add a method to a list of methods by name. > - * For a particular class we are keeping track > - * of all the methods with the same name. > - */ > - public void add(Method method) > - { > - String methodName = method.getName(); > - > - List l = (List) methodByNameMap.get( methodName ); > - > - if ( l == null) > - { > - l = new ArrayList(); > - methodByNameMap.put(methodName, l); > - } > - > - l.add(method); > - > - return; > - } > - > - /** > - * Return a list of methods with the same name. > - * > - * @param String key > - * @return List list of methods > - */ > - public List get(String key) > - { > - return (List) methodByNameMap.get(key); > - } > - > - /** > - * <p> > - * Find a method. Attempts to find the > - * most appropriate method using the > - * sense of 'specificity'. > - * </p> > - * > - * <p> > - * This turns out to be a relatively rare case > - * where this is needed - however, functionality > - * like this is needed. This may not be the > - * optimum approach, but it works. > - * </p> > - * > - * @param String name of method > - * @param Object[] params > - * @return Method > - */ > - public Method find(String methodName, Object[] params) > - throws AmbiguousException > - { > - List methodList = (List) methodByNameMap.get(methodName); > - > - if (methodList == null) > - { > - return null; > - } > + private static final int MORE_SPECIFIC = 0; > + private static final int LESS_SPECIFIC = 1; > + private static final int INCOMPARABLE = 2; > + > + /** > + * Keep track of all methods with the same name. > + */ > + Map methodByNameMap = new Hashtable(); > + > + /** > + * Add a method to a list of methods by name. > + * For a particular class we are keeping track > + * of all the methods with the same name. > + */ > + public void add(Method method) > + { > + String methodName = method.getName(); > + > + List l = get( methodName ); > + > + if ( l == null) > + { > + l = new ArrayList(); > + methodByNameMap.put(methodName, l); > + } > + > + l.add(method); > + > + return; > + } > + > + /** > + * Return a list of methods with the same name. > + * > + * @param String key > + * @return List list of methods > + */ > + public List get(String key) > + { > + return (List) methodByNameMap.get(key); > + } > + > + /** > + * <p> > + * Find a method. Attempts to find the > + * most specific applicable method using the > + * algorithm described in the JLS section > + * 15.12.2 (with the exception that it can't > + * distinguish a primitive type argument from > + * an object type argument, since in reflection > + * primitive type arguments are represented by > + * their object counterparts, so for an argument of > + * type (say) java.lang.Integer, it will not be able > + * to decide between a method that takes int and a > + * method that takes java.lang.Integer as a parameter. > + * </p> > + * > + * <p> > + * This turns out to be a relatively rare case > + * where this is needed - however, functionality > + * like this is needed. > + * </p> > + * > + * @param methodName name of method > + * @param args the actual arguments with which the method is called > + * @return the most specific applicable method, or null if no > + * method is applicable. > + * @throws AmbiguousException if there is more than one maximally > + * specific applicable method > + */ > + public Method find(String methodName, Object[] args) > + throws AmbiguousException > + { > + List methodList = get(methodName); > + > + if (methodList == null) > + { > + return null; > + } > + > + int l = args.length; > + Class[] classes = new Class[l]; > + > + for(int i = 0; i < l; ++i) > + { > + Object arg = args[i]; > + // A null argument is always treated as being a generic Object. > + classes[i] = > + arg == null ? java.lang.Object.class : arg.getClass(); > + } > + > + return getMostSpecific(methodList, classes); > + } > + > + /** > + * simple distinguishable exception, used when > + * we run across ambiguous overloading > + */ > + public static class AmbiguousException extends Exception > + { > + } > + > + > + private static Method getMostSpecific(List methods, Class[] classes) > + throws AmbiguousException > + { > + LinkedList applicables = getApplicables(methods, classes); > + > + if(applicables.isEmpty()) > + { > + return null; > + } > + > + if(applicables.size() == 1) > + { > + return (Method)applicables.getFirst(); > + } > > - Class[] parameterTypes = null; > - Method method = null; > + /* > + * This list will contain the maximally specific methods. Hopefully at > + * the end of the below loop, the list will contain exactly one method, > + * (the most specific method) otherwise we have ambiguity. > + */ > + > + LinkedList maximals = new LinkedList(); > > - int numMethods = methodList.size(); > - > - int bestDistance = -2; > - Method bestMethod = null; > - Twonk bestTwonk = null; > - boolean ambiguous = false; > - > - for (int i = 0; i < numMethods; i++) > - { > - method = (Method) methodList.get(i); > - parameterTypes = method.getParameterTypes(); > - > - /* > - * The methods we are trying to compare must > - * the same number of arguments. > - */ > + for (Iterator applicable = applicables.iterator(); > + applicable.hasNext();) > + { > + Method app = (Method) applicable.next(); > + Class[] appArgs = app.getParameterTypes(); > + boolean lessSpecific = false; > + > + for (Iterator maximal = maximals.iterator(); > + !lessSpecific && maximal.hasNext();) > + { > + Method max = (Method) maximal.next(); > > - if (parameterTypes.length == params.length) > - { > - /* > - * use the calling parameters as the baseline > - * and calculate the 'distance' from the parameters > - * to the method args. This will be useful when > - * determining specificity > - */ > - > - Twonk twonk = calcDistance( params, parameterTypes ); > - > - if (twonk != null ) > + switch(moreSpecific(appArgs, max.getParameterTypes())) > { > - /* > - * if we don't have anything yet, take it > - */ > - > - if ( bestTwonk == null ) > - { > - bestTwonk = twonk; > - bestMethod = method; > - } > - else > - { > + case MORE_SPECIFIC: > + { > /* > - * now see which is more specific, this current > - * versus what we think of as the best candidate > + * This method is more specific than the previously > + * known maximally specific, so remove the old maximum. > */ > - > - int val = twonk.moreSpecific( bestTwonk ); > - > - //System.out.println("Val = " + val + " for " + method + >" vs " + bestMethod ); > - > - if( val == 0) > - { > - /* > - * this means that the parameters 'crossed' > - * therefore, it's ambiguous because one is as > - * good as the other > - */ > - ambiguous = true; > - } > - else if ( val == 1) > - { > - /* > - * the current method is clearly more > - * specific than the current best, so > - * we take the current we are testing > - * and clear the ambiguity flag > - */ > - ambiguous = false; > - bestTwonk = twonk; > - bestMethod = method; > - } > - } > - } > - > + > + maximal.remove(); > + break; > + } > + > + case LESS_SPECIFIC: > + { > + /* > + * This method is less specific than some of the > + * currently known maximally specific methods, so we > + * won't add it into the set of maximally specific > + * methods > + */ > + > + lessSpecific = true; > + break; > + } > + } > } > + > + if(!lessSpecific) > + { > + maximals.addLast(app); > + } > } > - > - /* > - * if ambiguous is true, it means we couldn't decide > - * so inform the caller... > - */ > - > - if ( ambiguous ) > - { > - throw new AmbiguousException(); > + > + if(maximals.size() > 1) > + { > + // We have more than one maximally specific method > + throw new AmbiguousException(); > + } > + > + return (Method)maximals.getFirst(); > + } > + > + /** > + * Determines which method signature (represented by a class array) is more > + * specific. This defines a partial ordering on the method signatures. > + * @param c1 first signature to compare > + * @param c2 second signature to compare > + * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if > + * c1 is less specific than c2, INCOMPARABLE if they are incomparable. > + */ > + private static int moreSpecific(Class[] c1, Class[] c2) > + { > + boolean c1MoreSpecific = false; > + boolean c2MoreSpecific = false; > + > + for(int i = 0; i < c1.length; ++i) > + { > + if(c1[i] != c2[i]) > + { > + c1MoreSpecific = > + c1MoreSpecific || > + isStrictMethodInvocationConvertible(c2[i], c1[i]); > + c2MoreSpecific = > + c2MoreSpecific || > + isStrictMethodInvocationConvertible(c1[i], c2[i]); > + } > } > - > - return bestMethod; > - } > - > - /** > - * Calculates the distance, expressed as a vector of inheritance > - * steps, between the calling args and the method args. > - * There still is an issue re interfaces... > - */ > - private Twonk calcDistance( Object[] set, Class[] base ) > - { > - if ( set.length != base.length) > - return null; > - > - Twonk twonk = new Twonk( set.length ); > - > - int distance = 0; > - > - for (int i = 0; i < set.length; i++) > - { > - /* > - * can I get from here to there? > - */ > - > - Class setclass = set[i].getClass(); > - > - if ( !base[i].isAssignableFrom( set[i].getClass() )) > - return null; > - > - /* > - * ok, I can. How many steps? > - */ > - > - Class c = setclass; > - > - while( c != null) > - { > + > + if(c1MoreSpecific) > + { > + if(c2MoreSpecific) > + { > /* > - * is this a valid step? > + * Incomparable due to cross-assignable arguments (i.e. > + * foo(String, Object) vs. foo(Object, String)) > */ > - > - if ( !base[i].isAssignableFrom( c ) ) > - { > - /* > - * it stopped being assignable - therefore we are looking at > - * an interface as our target, so move back one step > - * from the distance as the stop wasn't valid > - */ > - break; > - } > - > - if( base[i].equals( c ) ) > - { > - /* > - * we are equal, so no need to move forward > - */ > - > - break; > - } > - > - c = c.getSuperclass(); > - twonk.distance++; > - twonk.vec[i]++; > + > + return INCOMPARABLE; > } > - } > - > - return twonk; > - } > - > - /** > - * simple distinguishable exception, used when > - * we run across ambiguous overloading > - */ > - public class AmbiguousException extends Exception > - { > - } > - > - /** > - * little class to hold 'distance' information > - * for calling params, as well as determine > - * specificity > - */ > - private class Twonk > - { > - public int distance; > - public int[] vec; > - > - public Twonk( int size ) > - { > - vec = new int[size]; > + > + return MORE_SPECIFIC; > } > - > - public int moreSpecific( Twonk other ) > - { > - if (other.vec.length != vec.length ) > - return -1; > - > - boolean low = false; > - boolean high = false; > - > - for (int i = 0; i < vec.length; i++) > - { > - if ( vec[i] > other.vec[i]) > - { > - high = true; > - } > - else if (vec[i] < other.vec[i] ) > - { > - low = true; > - } > - } > - > - /* > - * this is a 'crossing' - meaning that > - * we saw the parameter 'slopes' cross > - * this means ambiguity > - */ > - > - if (high && low) > - return 0; > - > - /* > - * we saw that all args were 'high', meaning > - * that the other method is more specific so > - * we are less > - */ > - > - if( high && !low) > - return -1; > - > - /* > - * we saw that all points were lower, therefore > - * we are more specific > - */ > - > - if( !high && low ) > - return 1; > - > - /* > - * the remainder, neither high or low > - * means we are the same. This really can't > - * happen, as it implies the same args, right? > - */ > - > - return 1; > + > + if(c2MoreSpecific) > + { > + return LESS_SPECIFIC; > + } > + > + /* > + * Incomparable due to non-related arguments (i.e. > + * foo(Runnable) vs. foo(Serializable)) > + */ > + > + return INCOMPARABLE; > + } > + > + /** > + * Returns all methods that are applicable to actual argument types. > + * @param methods list of all candidate methods > + * @param classes the actual types of the arguments > + * @return a list that contains only applicable methods (number of > + * formal and actual arguments matches, and argument types are assignable > + * to formal types through a method invocation conversion). > + */ > + private static LinkedList getApplicables(List methods, Class[] classes) > + { > + LinkedList list = new LinkedList(); > + > + for (Iterator imethod = methods.iterator(); imethod.hasNext();) > + { > + Method method = (Method) imethod.next(); > + > + if(isApplicable(method, classes)) > + { > + list.add(method); > + } > + > + } > + return list; > + } > + > + /** > + * Returns true if the supplied method is applicable to actual > + * argument types. > + */ > + private static boolean isApplicable(Method method, Class[] classes) > + { > + Class[] methodArgs = method.getParameterTypes(); > + > + if(methodArgs.length != classes.length) > + { > + return false; > + } > + > + for(int i = 0; i < classes.length; ++i) > + { > + if(!isMethodInvocationConvertible(methodArgs[i], classes[i])) > + { > + 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) > + { > + /* > + * Check for identity or widening reference conversion > + */ > + > + if(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; > + } > + 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. > + * @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) > + { > + /* > + * 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; > + } > + return false; > + } > +} > > > > > -- > To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> > For additional commands, e-mail: <mailto:[EMAIL PROTECTED]> -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
