You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by ge...@apache.org on 2001/11/26 17:00:19 UTC

cvs commit: jakarta-velocity/src/java/org/apache/velocity/util/introspection MethodMap.java

geirm       01/11/26 08:00:19

  Modified:    src/java/org/apache/velocity/util/introspection
                        MethodMap.java
  Log:
  Rewrite to try to do finds based on 'specificity', to help resolve when
  a method is overloaded.  Seems to work with no loss of performance, as
  ClassMap does caching by signature, so once fond for a given calling
  sig, its taken from the cache.
  
  I will redo how it notes ambiguity - will throw an exception to caller, and
  let the caller deal.  That way we don't assume that a log message is ok, and
  can back out the CTOR changes.
  
  Revision  Changes    Path
  1.12      +240 -28   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.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- MethodMap.java	2001/10/22 03:53:27	1.11
  +++ MethodMap.java	2001/11/26 16:00:19	1.12
  @@ -61,17 +61,38 @@
   
   import java.lang.reflect.Method;
   
  +import org.apache.velocity.runtime.RuntimeServices;
  +
   /**
    *
    * @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>
  - * @version $Id: MethodMap.java,v 1.11 2001/10/22 03:53:27 jon Exp $
  + * @version $Id: MethodMap.java,v 1.12 2001/11/26 16:00:19 geirm Exp $
    */
  -
   public class MethodMap
   {
  +    protected Class clazz = null;
  +    protected RuntimeServices rsvc = null;
  +
  +    /**
  +     *  we need both the class and rsvc for ambiguity
  +     *  reporting
  +     */
  +    public MethodMap( RuntimeServices rsvc, Class c )
  +    {
  +        this.clazz = c;
  +        this.rsvc = rsvc;
  +    }
  +
  +    /**
  +     *  hide this...
  +     */
  +    private MethodMap()
  +    {
  +    }
  +
       /**
        * Keep track of all methods with the same name.
        */
  @@ -111,11 +132,22 @@
       }
   
       /**
  -     * Find a method.
  +     *  <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
  +     *  @param String name of method
  +     *  @param Object[] params
  +     *  @return Method
        */
       public Method find(String methodName, Object[] params)
       {
  @@ -131,6 +163,11 @@
   
           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);
  @@ -143,37 +180,212 @@
   
               if (parameterTypes.length == params.length)
               {
  -                /* 
  -                 * Make sure the given parameter is a valid
  -                 * subclass of the method parameter in question.
  +                /*
  +                 *  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
                    */
  -
  -                for (int j = 0; ; j++)
  +                 
  +                Twonk twonk = calcDistance( params, parameterTypes );
  +                
  +                if (twonk != null )
                   {
  -                    if (j >= parameterTypes.length)
  -                        return method;
  -
  -                    Class c = parameterTypes[j];
  -                    Object p = params[j];
  -                    if ( c.isPrimitive() )
  +                    /*
  +                     *  if we don't have anything yet, take it
  +                     */
  +                     
  +                    if ( bestTwonk == null )
  +                    {
  +                        bestTwonk = twonk;
  +                        bestMethod = method;
  +                    }
  +                    else
                       {
  -                        try
  +                        /*
  +                         * now see which is more specific, this current
  +                         * versus what we think of as the best candidate
  +                         */
  +                         
  +                        int val = twonk.moreSpecific( bestTwonk );
  +                         
  +                        //System.out.println("Val = " + val + " for " + method + " vs " + bestMethod );
  +                            
  +                        if( val == 0)
                           {
  -                            if ( c != p.getClass().getField("TYPE").get(p) )
  -                                break;
  -                        } 
  -                        catch (Exception ex) 
  +                            /*
  +                             * this means that the parameters 'crossed'
  +                             * therefore, it's ambiguous because one is as 
  +                             * good as the other
  +                             */
  +                            ambiguous = true;
  +                        }
  +                        else if ( val == 1)
                           {
  -                            break; // p is not a primitive derivate
  +                            /*
  +                             *  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;
                           }
                       }
  -                    else if ( (p != null) &&
  -                              !c.isAssignableFrom( p.getClass() ) )
  -                        break;
  -                }
  +                }        
  +               
               }
           }
  +
  +        if ( ambiguous )
  +        {    
  +            String msg = "MethodMap : Ambiguous method invocation "
  +                + methodName + "( ";
  +
  +            for (int i = 0; i < params.length; i++)
  +            {
  +                if ( i > 0)
  +                    msg = msg + ", ";
  +                
  +                msg = msg + params[i].getClass().getName();
  +            }
  +
  +            msg = msg + ") for class " + clazz;
  +
  +            rsvc.error( msg );
  +
  +            System.out.println( msg );
  +
  +            return null;
  +        }
  +           
  +        return bestMethod;
  +    }
  +    
  +    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)
  +            {      
  +                /*
  +                 * is this a valid step?
  +                 */
  +                 
  +                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;
  +                }
   
  -        return null;
  +                c = c.getSuperclass();
  +                twonk.distance++;
  +                twonk.vec[i]++;
  +            }
  +         }
  +                
  +        return twonk;
  +    }
  +    
  +    class Twonk
  +    {
  +        public int distance;
  +        public int[] vec;
  +        
  +        public Twonk( int size )
  +        {
  +            vec = new int[size];
  +        }
  +        
  +        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;
  +        }
       }
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>