View Javadoc

    1   /*
    2    $Id: MetaClass.java,v 1.85 2004/12/11 20:38:46 blackdrag Exp $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    44
    45   */
    46  package groovy.lang;
    47
    48  import java.beans.BeanInfo;
    49  import java.beans.EventSetDescriptor;
    50  import java.beans.IntrospectionException;
    51  import java.beans.Introspector;
    52  import java.beans.PropertyDescriptor;
    53  import java.lang.reflect.Array;
    54  import java.lang.reflect.Constructor;
    55  import java.lang.reflect.Field;
    56  import java.lang.reflect.InvocationHandler;
    57  import java.lang.reflect.InvocationTargetException;
    58  import java.lang.reflect.Method;
    59  import java.lang.reflect.Modifier;
    60  import java.lang.reflect.Proxy;
    61  import java.math.BigDecimal;
    62  import java.math.BigInteger;
    63  import java.net.URL;
    64  import java.security.AccessControlException;
    65  import java.security.AccessController;
    66  import java.security.PrivilegedAction;
    67  import java.security.PrivilegedActionException;
    68  import java.security.PrivilegedExceptionAction;
    69  import java.util.ArrayList;
    70  import java.util.Arrays;
    71  import java.util.Collection;
    72  import java.util.Collections;
    73  import java.util.HashMap;
    74  import java.util.Iterator;
    75  import java.util.List;
    76  import java.util.Map;
    77  import java.util.logging.Logger;
    78
    79  import org.codehaus.groovy.ast.ClassNode;
    80  import org.codehaus.groovy.classgen.ReflectorGenerator;
    81  import org.codehaus.groovy.control.CompilationUnit;
    82  import org.codehaus.groovy.control.Phases;
    83  import org.codehaus.groovy.runtime.ClosureListener;
    84  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    85  import org.codehaus.groovy.runtime.GroovyCategorySupport;
    86  import org.codehaus.groovy.runtime.InvokerHelper;
    87  import org.codehaus.groovy.runtime.InvokerInvocationException;
    88  import org.codehaus.groovy.runtime.MethodClosure;
    89  import org.codehaus.groovy.runtime.MethodHelper;
    90  import org.codehaus.groovy.runtime.MethodKey;
    91  import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
    92  import org.codehaus.groovy.runtime.NewStaticMetaMethod;
    93  import org.codehaus.groovy.runtime.ReflectionMetaMethod;
    94  import org.codehaus.groovy.runtime.Reflector;
    95  import org.codehaus.groovy.runtime.TemporaryMethodKey;
    96  import org.codehaus.groovy.runtime.TransformMetaMethod;
    97  import org.objectweb.asm.ClassVisitor;
    98  import org.objectweb.asm.ClassWriter;
    99
    100 /***
    101  * Allows methods to be dynamically added to existing classes at runtime
    102  *
    103  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    104  * @author Guillaume Laforge
    105  * @version $Revision: 1.85 $
    106  */
    107 public class MetaClass {
    108
    109     private static final Logger log = Logger.getLogger(MetaClass.class.getName());
    110
    111     public static final Object[] EMPTY_ARRAY = {
    112     };
    113     public static Class[] EMPTY_TYPE_ARRAY = {
    114     };
    115     protected static final Object[] ARRAY_WITH_NULL = { null };
    116
    117     private static boolean useReflection = false;
    118
    119     private MetaClassRegistry registry;
    120     private Class theClass;
    121     private ClassNode classNode;
    122     private Map methodIndex = new HashMap();
    123     private Map staticMethodIndex = new HashMap();
    124     private List newGroovyMethodsList = new ArrayList();
    125     //private Map propertyDescriptors = Collections.synchronizedMap(new HashMap());
    126     private Map propertyMap = Collections.synchronizedMap(new HashMap());
    127     private Map listeners = new HashMap();
    128     private Map methodCache = Collections.synchronizedMap(new HashMap());
    129     private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
    130     private MetaMethod genericGetMethod;
    131     private MetaMethod genericSetMethod;
    132     private List constructors;
    133     private List allMethods = new ArrayList();
    134     private List interfaceMethods;
    135     private Reflector reflector;
    136     private boolean initialised;
    137 	// we only need one of these that can be reused over and over.
    138 	private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
    139
    140     public MetaClass(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
    141         this.registry = registry;
    142         this.theClass = theClass;
    143
    144         constructors = Arrays.asList(theClass.getDeclaredConstructors());
    145         addMethods(theClass);
    146
    147         // introspect
    148         BeanInfo info = null;
    149         try {
    150             info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
    151                 public Object run() throws IntrospectionException {
    152                     return Introspector.getBeanInfo(theClass);
    153                 }
    154             });
    155         } catch (PrivilegedActionException pae) {
    156             if (pae.getException() instanceof IntrospectionException) {
    157                 throw (IntrospectionException) pae.getException();
    158             } else {
    159                 throw new RuntimeException(pae.getException());
    160             }
    161         }
    162
    163         PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
    164
    165         // build up the metaproperties based on the public fields, property descriptors,
    166         // and the getters and setters
    167         setupProperties(descriptors);
    168
    169         /* old code
    170         for (int i = 0; i < descriptors.length; i++) {
    171             PropertyDescriptor descriptor = descriptors[i];
    172             propertyDescriptors.put(descriptor.getName(), descriptor);
    173         }
    174         */
    175
    176         EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
    177         for (int i = 0; i < eventDescriptors.length; i++) {
    178             EventSetDescriptor descriptor = eventDescriptors[i];
    179             Method[] listenerMethods = descriptor.getListenerMethods();
    180             for (int j = 0; j < listenerMethods.length; j++) {
    181                 Method listenerMethod = listenerMethods[j];
    182                 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
    183                 listeners.put(listenerMethod.getName(), metaMethod);
    184             }
    185         }
    186     }
    187
    188     public static boolean isUseReflection() {
    189         return useReflection;
    190     }
    191
    192     /***
    193      * Allows reflection to be enabled in situations where bytecode generation
    194      * of method invocations causes issues.
    195      *
    196      * @param useReflection
    197      */
    198     public static void setUseReflection(boolean useReflection) {
    199         MetaClass.useReflection = useReflection;
    200     }
    201
    202     private void addInheritedMethods(Class theClass) {
    203         // lets add all the base class methods
    204         Class c = theClass;
    205         if (c != Object.class) {
    206             while (true) {
    207                 c = c.getSuperclass();
    208                 if (c == Object.class || c == null) {
    209                     break;
    210                 }
    211                 addMethods(c);
    212                 addNewStaticMethodsFrom(c);
    213
    214             }
    215         }
    216
    217         // now lets see if there are any methods on one of my interfaces
    218         Class[] interfaces = theClass.getInterfaces();
    219         for (int i = 0; i < interfaces.length; i++) {
    220             addNewStaticMethodsFrom(interfaces[i]);
    221         }
    222
    223         // lets add Object methods after interfaces, as all interfaces derive from Object.
    224         // this ensures List and Collection methods come before Object etc
    225         if (theClass != Object.class) {
    226             addMethods(Object.class);
    227             addNewStaticMethodsFrom(Object.class);
    228         }
    229
    230         if (theClass.isArray() && !theClass.equals(Object[].class)) {
    231             addNewStaticMethodsFrom(Object[].class);
    232         }
    233     }
    234
    235     /***
    236      * @return all the normal instance methods avaiable on this class for the
    237      *         given name
    238      */
    239     public List getMethods(String name) {
    240         List answer = (List) methodIndex.get(name);
    241         List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
    242         if (used != null) {
    243             if (answer != null) {
    244                 answer.addAll(used);
    245             } else{
    246                 answer = used;
    247             }
    248         }
    249         if (answer == null) {
    250             answer = Collections.EMPTY_LIST;
    251         }
    252         return answer;
    253     }
    254
    255     /***
    256      * @return all the normal static methods avaiable on this class for the
    257      *         given name
    258      */
    259     public List getStaticMethods(String name) {
    260         List answer = (List) staticMethodIndex.get(name);
    261         if (answer == null) {
    262             return Collections.EMPTY_LIST;
    263         }
    264         return answer;
    265     }
    266
    267     /***
    268      * Allows static method definitions to be added to a meta class as if it
    269      * was an instance method
    270      *
    271      * @param method
    272      */
    273     protected void addNewInstanceMethod(Method method) {
    274         if (initialised) {
    275             throw new RuntimeException("Already initialized, cannot add new method: " + method);
    276         }
    277         else {
    278             NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
    279             addMethod(newMethod);
    280             addNewInstanceMethod(newMethod);
    281         }
    282     }
    283
    284     protected void addNewInstanceMethod(MetaMethod method) {
    285         newGroovyMethodsList.add(method);
    286     }
    287
    288     protected void addNewStaticMethod(Method method) {
    289         if (initialised) {
    290             throw new RuntimeException("Already initialized, cannot add new method: " + method);
    291         }
    292         else {
    293             NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
    294             addMethod(newMethod);
    295             addNewStaticMethod(newMethod);
    296         }
    297     }
    298
    299     protected void addNewStaticMethod(MetaMethod method) {
    300         newGroovyMethodsList.add(method);
    301     }
    302
    303     public Object invokeMethod(Object object, String methodName, Object arguments) {
    304         return invokeMethod(object, methodName, asArray(arguments));
    305     }
    306
    307     /***
    308      * Invokes the given method on the object.
    309      *
    310      */
    311     public Object invokeMethod(Object object, String methodName, Object[] arguments) {
    312         if (object == null) {
    313             throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
    314         }
    315
    316         MetaMethod method = retrieveMethod(object, methodName, arguments);
    317
    318         if (method != null) {
    319             return doMethodInvoke(object, method, arguments);
    320         } else {
    321             // if no method was found, try to find a closure defined as a field of the class and run it
    322             try {
    323                 Object value = this.getProperty(object, methodName);
    324                 if (value instanceof Closure) {
    325                     Closure closure = (Closure) value;
    326                     closure.setDelegate(this);
    327                     return closure.call(arguments);
    328                 }
    329                 else {
    330                     throw new MissingMethodException(methodName, theClass, arguments);
    331                 }
    332             }
    333             catch (Exception e) {
    334                 throw new MissingMethodException(methodName, theClass, arguments);
    335             }
    336         }
    337     }
    338
    339     protected MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
    340         // lets try use the cache to find the method
    341         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    342         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
    343         if (method == null) {
    344             method = pickMethod(owner, methodName, arguments);
    345             if (method != null && method.isCacheable()) {
    346                 methodCache.put(methodKey.createCopy(), method);
    347             }
    348         }
    349         return method;
    350     }
    351
    352     public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
    353         // lets try use the cache to find the method
    354         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    355         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
    356         if (method == null) {
    357             method = pickMethod(methodName, arguments); // todo shall call pickStaticMethod also?
    358             if (method != null && method.isCacheable()) {
    359                 methodCache.put(methodKey.createCopy(), method);
    360             }
    361         }
    362         return method;
    363     }
    364
    365     public Constructor retrieveConstructor(Class[] arguments) {
    366         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
    367         if (constructor != null) {
    368             return constructor;
    369         }
    370         else {
    371             constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
    372             if (constructor != null) {
    373                 return constructor;
    374             }
    375         }
    376         return null;
    377     }
    378
    379     public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
    380         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    381         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    382         if (method == null) {
    383             method = pickStaticMethod(methodName, arguments);
    384             if (method != null) {
    385                 staticMethodCache.put(methodKey.createCopy(), method);
    386             }
    387         }
    388         return method;
    389     }
    390     /***
    391      * Picks which method to invoke for the given object, method name and arguments
    392      */
    393     protected MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
    394         MetaMethod method = null;
    395         List methods = getMethods(methodName);
    396         if (!methods.isEmpty()) {
    397             Class[] argClasses = convertToTypeArray(arguments);
    398             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, false);
    399             if (method == null) {
    400                 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
    401                 if (method == null) {
    402                     int size = (arguments != null) ? arguments.length : 0;
    403                     if (size == 1) {
    404                         Object firstArgument = arguments[0];
    405                         if (firstArgument instanceof List) {
    406                             // lets coerce the list arguments into an array of
    407                             // arguments
    408                             // e.g. calling JFrame.setLocation( [100, 100] )
    409
    410                             List list = (List) firstArgument;
    411                             arguments = list.toArray();
    412                             argClasses = convertToTypeArray(arguments);
    413                             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
    414                             if (method==null) return null;
    415                             return new TransformMetaMethod(method) {
    416                                 public Object invoke(Object object, Object[] arguments) throws Exception {
    417                                     Object firstArgument = arguments[0];
    418                                     List list = (List) firstArgument;
    419                                     arguments = list.toArray();
    420                                     return super.invoke(object, arguments);
    421                                 }
    422                             };
    423                         }
    424                     }
    425                 }
    426             }
    427         }
    428         return method;
    429     }
    430
    431     /***
    432      * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
    433      * this method is used only by ClassGenerator for static binding
    434      * @param methodName
    435      * @param arguments
    436      * @return
    437      */
    438     protected MetaMethod pickMethod(String methodName, Class[] arguments) {
    439         MetaMethod method = null;
    440         List methods = getMethods(methodName);
    441         if (!methods.isEmpty()) {
    442             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    443 // no coersion at classgen time.
    444 //            if (method == null) {
    445 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
    446 //            }
    447         }
    448         return method;
    449     }
    450
    451     public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
    452         //        System.out.println("Calling static method: " + methodName + " on args: " + InvokerHelper.toString(arguments));
    453         //        Class type = arguments == null ? null : arguments.getClass();
    454         //        System.out.println("Argument  type: " + type);
    455         //        System.out.println("Type of first arg: " + arguments[0] + " type: " + arguments[0].getClass());
    456
    457         // lets try use the cache to find the method
    458         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    459         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    460         if (method == null) {
    461             method = pickStaticMethod(object, methodName, arguments);
    462             if (method != null) {
    463                 staticMethodCache.put(methodKey.createCopy(), method);
    464             }
    465         }
    466
    467         if (method != null) {
    468             return doMethodInvoke(object, method, arguments);
    469         }
    470         /*
    471         List methods = getStaticMethods(methodName);
    472
    473         if (!methods.isEmpty()) {
    474             MetaMethod method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    475             if (method != null) {
    476                 return doMethodInvoke(theClass, method, arguments);
    477             }
    478         }
    479
    480         if (theClass != Class.class) {
    481             try {
    482                 return registry.getMetaClass(Class.class).invokeMethod(object, methodName, arguments);
    483             }
    484             catch (GroovyRuntimeException e) {
    485                 // throw our own exception
    486             }
    487         }
    488         */
    489         throw new MissingMethodException(methodName, theClass, arguments);
    490     }
    491
    492     protected MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
    493         MetaMethod method = null;
    494         List methods = getStaticMethods(methodName);
    495
    496         if (!methods.isEmpty()) {
    497             method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false);
    498         }
    499
    500         if (method == null && theClass != Class.class) {
    501             MetaClass classMetaClass = registry.getMetaClass(Class.class);
    502             method = classMetaClass.pickMethod(object, methodName, arguments);
    503         }
    504         return method;
    505     }
    506
    507     protected MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
    508         MetaMethod method = null;
    509         List methods = getStaticMethods(methodName);
    510
    511         if (!methods.isEmpty()) {
    512             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    513 // disabled to keep consistant with the original version of pickStatciMethod
    514 //            if (method == null) {
    515 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
    516 //            }
    517         }
    518
    519         if (method == null && theClass != Class.class) {
    520             MetaClass classMetaClass = registry.getMetaClass(Class.class);
    521             method = classMetaClass.pickMethod(methodName, arguments);
    522         }
    523         return method;
    524     }
    525
    526     public Object invokeConstructor(Object[] arguments) {
    527         Class[] argClasses = convertToTypeArray(arguments);
    528         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
    529         if (constructor != null) {
    530             return doConstructorInvoke(constructor, arguments);
    531         }
    532         else {
    533             constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
    534             if (constructor != null) {
    535                 return doConstructorInvoke(constructor, arguments);
    536             }
    537         }
    538
    539         if (arguments.length == 1) {
    540             Object firstArgument = arguments[0];
    541             if (firstArgument instanceof Map) {
    542                 constructor = (Constructor) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false);
    543                 if (constructor != null) {
    544                     Object bean = doConstructorInvoke(constructor, EMPTY_ARRAY);
    545                     setProperties(bean, ((Map) firstArgument));
    546                     return bean;
    547                 }
    548             }
    549         }
    550         throw new GroovyRuntimeException(
    551                     "Could not find matching constructor for: "
    552                         + theClass.getName()
    553                         + "("+InvokerHelper.toTypeString(arguments)+")");
    554     }
    555
    556     /***
    557      * Sets a number of bean properties from the given Map where the keys are
    558      * the String names of properties and the values are the values of the
    559      * properties to set
    560      */
    561     public void setProperties(Object bean, Map map) {
    562         for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
    563             Map.Entry entry = (Map.Entry) iter.next();
    564             String key = entry.getKey().toString();
    565
    566             // do we have this property?
    567             if(propertyMap.get(key) == null)
    568                 continue;
    569
    570             Object value = entry.getValue();
    571             try {
    572                 setProperty(bean, key, value);
    573             }
    574             catch (GroovyRuntimeException e) {
    575                 // lets ignore missing properties
    576                 /*** todo should replace this code with a getMetaProperty(key) != null check
    577                  i.e. don't try and set a non-existent property
    578                  */
    579             }
    580         }
    581     }
    582
    583     /***
    584      * @return the given property's value on the object
    585      */
    586     public Object getProperty(final Object object, final String property) {
    587         /* old code; we now use the metaProperty map to handle properties
    588         MetaMethod metaMethod = null;
    589         PropertyDescriptor descriptor = (PropertyDescriptor) propertyDescriptors.get(property);
    590         if (descriptor != null) {
    591             Method method = descriptor.getReadMethod();
    592             if (method == null) {
    593                 throw new GroovyRuntimeException("Cannot read property: " + property);
    594             }
    595             metaMethod = findMethod(method);
    596             if (metaMethod == null) {
    597                 // lets try invoke a static getter method
    598                 metaMethod = findGetter(object, "get" + capitalize(property));
    599             }
    600             if (metaMethod != null) {
    601                 return doMethodInvoke(object, metaMethod, EMPTY_ARRAY);
    602             }
    603         }
    604         */
    605
    606         // look for the property in our map
    607         MetaProperty mp = (MetaProperty) propertyMap.get(property);
    608         if(mp != null) {
    609             try {
    610                 //System.out.println("we found a metaproperty for " + theClass.getName() +
    611                 //  "." + property);
    612                 // delegate the get operation to the metaproperty
    613                 return mp.getProperty(object);
    614             }
    615             catch(Exception e) {
    616                 throw new GroovyRuntimeException("Cannot read property: " + property);
    617             }
    618         }
    619
    620         if (genericGetMethod == null) {
    621             // Make sure there isn't a generic method in the "use" cases
    622             List possibleGenericMethods = getMethods("get");
    623             if (possibleGenericMethods != null) {
    624                 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
    625                     MetaMethod mmethod = (MetaMethod) i.next();
    626                     Class[] paramTypes = mmethod.getParameterTypes();
    627                     if (paramTypes.length == 1 && paramTypes[0] == String.class) {
    628                         Object[] arguments = {property};
    629                         Object answer = doMethodInvoke(object, mmethod, arguments);
    630                         return answer;
    631                     }
    632                 }
    633             }
    634         }
    635         else {
    636             Object[] arguments = { property };
    637             Object answer = doMethodInvoke(object, genericGetMethod, arguments);
    638             // jes bug? a property retrieved via a generic get() can't have a null value?
    639             if (answer != null) {
    640                 return answer;
    641             }
    642         }
    643
    644         // is the property the name of a method - in which case return a
    645         // closure
    646         List methods = getMethods(property);
    647         if (!methods.isEmpty()) {
    648             return new MethodClosure(object, property);
    649         }
    650
    651         // lets try invoke a static getter method
    652         // this case is for protected fields. I wish there was a better way...
    653         Exception lastException = null;
    654         try {
    655             MetaMethod method = findGetter(object, "get" + capitalize(property));
    656             if (method != null) {
    657                 return doMethodInvoke(object, method, EMPTY_ARRAY);
    658             }
    659         }
    660         catch (GroovyRuntimeException e) {
    661             lastException = e;
    662         }
    663
    664         /*** todo or are we an extensible groovy class? */
    665         if (genericGetMethod != null) {
    666             return null;
    667         }
    668         else {
    669             /*** todo these special cases should be special MetaClasses maybe */
    670             if (object instanceof Class) {
    671                 // lets try a static field
    672                 return getStaticProperty((Class) object, property);
    673             }
    674             if (object instanceof Collection) {
    675                 return DefaultGroovyMethods.getAt((Collection) object, property);
    676             }
    677             if (object instanceof Object[]) {
    678                 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
    679             }
    680             if (object instanceof Object) {
    681                 try {
    682                     // lets try a public field
    683                     Field field = object.getClass().getDeclaredField(property);
    684                     return field.get(object);
    685                 }
    686                 catch (Exception e1) {
    687                     // fall through
    688                 }
    689             }
    690
    691             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
    692             if (addListenerMethod != null) {
    693                 /* @todo one day we could try return the previously registered Closure listener for easy removal */
    694                 return null;
    695             }
    696
    697             if (lastException == null)
    698                 throw new MissingPropertyException(property, theClass);
    699             else
    700                 throw new MissingPropertyException(property, theClass, lastException);
    701         }
    702     }
    703
    704     /***
    705      * Get all the properties defined for this type
    706      * @return a list of MetaProperty objects
    707      */
    708     public List getProperties() {
    709         // simply return the values of the metaproperty map as a List
    710         return new ArrayList(propertyMap.values());
    711     }
    712
    713     /***
    714      * This will build up the property map (Map of MetaProperty objects, keyed on
    715      * property name).
    716      */
    717     protected void setupProperties(PropertyDescriptor[] propertyDescriptors) {
    718         MetaProperty mp;
    719         Method method;
    720         MetaMethod getter = null;
    721         MetaMethod setter = null;
    722         Class klass;
    723
    724         // first get the public fields and create MetaFieldProperty objects
    725         klass = theClass;
    726         while(klass != null) {
    727             Field[] fields = klass.getDeclaredFields();
    728             for(int i = 0; i < fields.length; i++) {
    729                 // we're only interested in publics
    730                 if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0)
    731                     continue;
    732
    733                 // see if we already got this
    734                 if(propertyMap.get(fields[i].getName()) != null)
    735                     continue;
    736
    737                 //System.out.println("adding field " + fields[i].getName() +
    738                 //  " for class " + klass.getName());
    739                 // stick it in there!
    740                 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
    741             }
    742
    743             // now get the super class
    744             klass = klass.getSuperclass();
    745         }
    746
    747 		// if this an Array, then add the special read-only "length" property
    748 		if(theClass.isArray()) {
    749 			propertyMap.put("length", arrayLengthProperty);
    750 		}
    751
    752         // now iterate over the map of property descriptors and generate
    753         // MetaBeanProperty objects
    754         for(int i=0; i<propertyDescriptors.length; i++) {
    755             PropertyDescriptor pd = propertyDescriptors[i];
    756             // skip if the field already exists in the map
    757             if(propertyMap.get(pd.getName()) != null)
    758                 continue;
    759
    760             // skip if the property type is unknown (this seems to be the case if the
    761             // property descriptor is based on a setX() method that has two parameters,
    762             // which is not a valid property)
    763             if(pd.getPropertyType() == null)
    764                 continue;
    765
    766             // get the getter method
    767             method = pd.getReadMethod();
    768             if(method != null)
    769                 getter = findMethod(method);
    770             else
    771                 getter = null;
    772
    773             // get the setter method
    774             method = pd.getWriteMethod();
    775             if(method != null)
    776                 setter = findMethod(method);
    777             else
    778                 setter = null;
    779
    780             // now create the MetaProperty object
    781             //System.out.println("creating a bean property for class " +
    782             //  theClass.getName() + ": " + pd.getName());
    783
    784             mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
    785
    786             // put it in the list
    787             propertyMap.put(pd.getName(), mp);
    788         }
    789
    790         // now look for any stray getters that may be used to define a property
    791         klass = theClass;
    792         while(klass != null) {
    793             Method[] methods = klass.getDeclaredMethods();
    794             for (int i = 0; i < methods.length; i++) {
    795                 // filter out the privates
    796                 if(Modifier.isPublic(methods[i].getModifiers()) == false)
    797                     continue;
    798
    799                 method = methods[i];
    800
    801                 String methodName = method.getName();
    802
    803                 // is this a getter?
    804                 if(methodName.startsWith("get") &&
    805                     methodName.length() > 3 &&
    806                     method.getParameterTypes().length == 0) {
    807
    808                     // get the name of the property
    809                     String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
    810
    811                     // is this property already accounted for?
    812                     mp = (MetaProperty) propertyMap.get(propName);
    813                     if(mp != null) {
    814                         // we may have already found the setter for this
    815                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
    816                             // update the getter method to this one
    817                             ((MetaBeanProperty) mp).setGetter(findMethod(method));
    818                         }
    819                     }
    820                     else {
    821                         // we need to create a new property object
    822                         // type of the property is what the get method returns
    823                         MetaBeanProperty mbp = new MetaBeanProperty(propName,
    824                             method.getReturnType(),
    825                             findMethod(method), null);
    826
    827                         // add it to the map
    828                         propertyMap.put(propName, mbp);
    829                     }
    830                 }
    831                 else if(methodName.startsWith("set") &&
    832                     methodName.length() > 3 &&
    833                     method.getParameterTypes().length == 1) {
    834
    835                     // get the name of the property
    836                     String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
    837
    838                     // did we already find the getter of this?
    839                     mp = (MetaProperty) propertyMap.get(propName);
    840                     if(mp != null) {
    841                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
    842                             // update the setter method to this one
    843                             ((MetaBeanProperty) mp).setSetter(findMethod(method));
    844                         }
    845                     }
    846                     else {
    847                         // this is a new property to add
    848                         MetaBeanProperty mbp = new MetaBeanProperty(propName,
    849                                                                     method.getParameterTypes()[0],
    850                                                                     null,
    851                                                                     findMethod(method));
    852
    853                         // add it to the map
    854                         propertyMap.put(propName, mbp);
    855                     }
    856                 }
    857             }
    858
    859             // now get the super class
    860             klass = klass.getSuperclass();
    861         }
    862     }
    863
    864     /***
    865      * Sets the property value on an object
    866      */
    867     public void setProperty(Object object, String property, Object newValue) {
    868         /* old code, replaced with new MetaProperty stuff...
    869         PropertyDescriptor descriptor = (PropertyDescriptor) propertyDescriptors.get(property);
    870
    871         if (descriptor != null) {
    872             Method method = descriptor.getWriteMethod();
    873             if (method == null) {
    874                 throw new ReadOnlyPropertyException(property, theClass);
    875             }
    876             MetaMethod metaMethod = findMethod(method);
    877             Object[] arguments = { newValue };
    878             try {
    879                 doMethodInvoke(object, metaMethod, arguments);
    880             }
    881             catch (GroovyRuntimeException e) {
    882                 // if the value is a List see if we can construct the value
    883                 // from a constructor
    884                 if (newValue instanceof List) {
    885                     List list = (List) newValue;
    886                     int params = list.size();
    887                     Constructor[] propConstructors = descriptor.getPropertyType().getConstructors();
    888                     for (int i = 0; i < propConstructors.length; i++) {
    889                         Constructor constructor = propConstructors[i];
    890                         if (constructor.getParameterTypes().length == params) {
    891                             Object value = doConstructorInvoke(constructor, list.toArray());
    892                             doMethodInvoke(object, metaMethod, new Object[] { value });
    893                             return;
    894                         }
    895                     }
    896
    897                     // if value is an array
    898                     Class parameterType = method.getParameterTypes()[0];
    899                     if (parameterType.isArray()) {
    900                         Object objArray = asPrimitiveArray(list, parameterType);
    901                         doMethodInvoke(object, metaMethod, new Object[]{
    902                             objArray
    903                         });
    904                         return;
    905                     }
    906                 }
    907
    908                 // if value is an multidimensional array
    909                 if (newValue.getClass().isArray()) {
    910                     List list = Arrays.asList((Object[])newValue);
    911
    912                     Class parameterType = method.getParameterTypes()[0];
    913                     Class arrayType = parameterType.getComponentType();
    914                     Object objArray = Array.newInstance(arrayType, list.size());
    915
    916                     for (int i = 0; i < list.size(); i++) {
    917                         List list2 =Arrays.asList((Object[]) list.get(i));
    918                         Object objArray2 = asPrimitiveArray(list2, arrayType);
    919                         Array.set(objArray, i, objArray2);
    920                     }
    921
    922                     doMethodInvoke(object, metaMethod, new Object[]{
    923                         objArray
    924                     });
    925                     return;
    926                 }
    927
    928                 throw new MissingPropertyException(property, theClass, e);
    929             }
    930             return;
    931         }
    932         */
    933
    934         MetaProperty mp = (MetaProperty) propertyMap.get(property);
    935         if(mp != null) {
    936             try {
    937                 mp.setProperty(object, newValue);
    938                 return;
    939             }
    940 			catch(ReadOnlyPropertyException e) {
    941 				// just rethrow it; there's nothing left to do here
    942 				throw e;
    943 			}
    944             catch (Exception e) {
    945                 // if the value is a List see if we can construct the value
    946                 // from a constructor
    947                 if (newValue == null)
    948                     return;
    949                 if (newValue instanceof List) {
    950                     List list = (List) newValue;
    951                     int params = list.size();
    952                     Constructor[] constructors = mp.getType().getConstructors();
    953                     for (int i = 0; i < constructors.length; i++) {
    954                         Constructor constructor = constructors[i];
    955                         if (constructor.getParameterTypes().length == params) {
    956                             Object value = doConstructorInvoke(constructor, list.toArray());
    957                             mp.setProperty(object, value);
    958                             return;
    959                         }
    960                     }
    961
    962                     // if value is an array
    963                     Class parameterType = mp.getType();
    964                     if (parameterType.isArray()) {
    965                         Object objArray = asPrimitiveArray(list, parameterType);
    966                         mp.setProperty(object, objArray);
    967                         return;
    968                     }
    969                 }
    970
    971                 // if value is an multidimensional array
    972                 // jes currently this logic only supports metabeansproperties and
    973                 // not metafieldproperties. It shouldn't be too hard to support
    974                 // the latter...
    975                 if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
    976                     MetaBeanProperty mbp = (MetaBeanProperty) mp;
    977                     List list = Arrays.asList((Object[])newValue);
    978                     MetaMethod setter = mbp.getSetter();
    979
    980                     Class parameterType = setter.getParameterTypes()[0];
    981                     Class arrayType = parameterType.getComponentType();
    982                     Object objArray = Array.newInstance(arrayType, list.size());
    983
    984                     for (int i = 0; i < list.size(); i++) {
    985                         List list2 =Arrays.asList((Object[]) list.get(i));
    986                         Object objArray2 = asPrimitiveArray(list2, arrayType);
    987                         Array.set(objArray, i, objArray2);
    988                     }
    989
    990                     doMethodInvoke(object, setter, new Object[]{
    991                         objArray
    992                     });
    993                     return;
    994                 }
    995
    996                 throw new MissingPropertyException(property, theClass, e);
    997             }
    998         }
    999
    1000         try {
    1001             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
    1002             if (addListenerMethod != null && newValue instanceof Closure) {
    1003                 // lets create a dynamic proxy
    1004                 Object proxy =
    1005                     createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
    1006                 doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
    1007                 return;
    1008             }
    1009
    1010             if (genericSetMethod == null) {
    1011                 // Make sure there isn't a generic method in the "use" cases
    1012                 List possibleGenericMethods = getMethods("set");
    1013                 if (possibleGenericMethods != null) {
    1014                     for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
    1015                         MetaMethod mmethod = (MetaMethod) i.next();
    1016                         Class[] paramTypes = mmethod.getParameterTypes();
    1017                         if (paramTypes.length == 2 && paramTypes[0] == String.class) {
    1018                             Object[] arguments = {property, newValue};
    1019                             Object answer = doMethodInvoke(object, mmethod, arguments);
    1020                             return;
    1021                         }
    1022                     }
    1023                 }
    1024             }
    1025             else {
    1026                 Object[] arguments = { property, newValue };
    1027                 doMethodInvoke(object, genericSetMethod, arguments);
    1028                 return;
    1029             }
    1030
    1031             /*** todo or are we an extensible class? */
    1032
    1033             // lets try invoke the set method
    1034             // this is kind of ugly: if it is a protected field, we fall
    1035             // all the way down to this klunky code. Need a better
    1036             // way to handle this situation...
    1037
    1038             String method = "set" + capitalize(property);
    1039             try {
    1040                 invokeMethod(object, method, new Object[] { newValue });
    1041             }
    1042             catch (MissingMethodException e1) {
    1043                 try {
    1044                     Field field = object.getClass().getDeclaredField(property);
    1045                     field.set(object, newValue);
    1046                 }
    1047                 catch (Exception e2) {
    1048                     throw new MissingPropertyException(property, theClass, e2);
    1049                 }
    1050             }
    1051
    1052         }
    1053         catch (GroovyRuntimeException e) {
    1054             throw new MissingPropertyException(property, theClass, e);
    1055         }
    1056
    1057         // if we got here, the damn thing just aint there...
    1058         throw new MissingPropertyException(property, theClass);
    1059     }
    1060
    1061     /***
    1062      * @param list
    1063      * @param parameterType
    1064      * @return
    1065      */
    1066     private Object asPrimitiveArray(List list, Class parameterType) {
    1067         Class arrayType = parameterType.getComponentType();
    1068         Object objArray = Array.newInstance(arrayType, list.size());
    1069         for (int i = 0; i < list.size(); i++) {
    1070             Object obj = list.get(i);
    1071             if (arrayType.isPrimitive()) {
    1072                 if (obj instanceof Integer) {
    1073                     Array.setInt(objArray, i, ((Integer) obj).intValue());
    1074                 }
    1075                 else if (obj instanceof Double) {
    1076                     Array.setDouble(objArray, i, ((Double) obj).doubleValue());
    1077                 }
    1078                 else if (obj instanceof Boolean) {
    1079                     Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
    1080                 }
    1081                 else if (obj instanceof Long) {
    1082                     Array.setLong(objArray, i, ((Long) obj).longValue());
    1083                 }
    1084                 else if (obj instanceof Float) {
    1085                     Array.setFloat(objArray, i, ((Float) obj).floatValue());
    1086                 }
    1087                 else if (obj instanceof Character) {
    1088                     Array.setChar(objArray, i, ((Character) obj).charValue());
    1089                 }
    1090                 else if (obj instanceof Byte) {
    1091                     Array.setByte(objArray, i, ((Byte) obj).byteValue());
    1092                 }
    1093                 else if (obj instanceof Short) {
    1094                     Array.setShort(objArray, i, ((Short) obj).shortValue());
    1095                 }
    1096             }
    1097             else {
    1098                 Array.set(objArray, i, obj);
    1099             }
    1100         }
    1101         return objArray;
    1102     }
    1103
    1104     public ClassNode getClassNode() {
    1105         if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
    1106             // lets try load it from the classpath
    1107             String className = theClass.getName();
    1108             String groovyFile = className;
    1109             int idx = groovyFile.indexOf('$');
    1110             if (idx > 0) {
    1111                 groovyFile = groovyFile.substring(0, idx);
    1112             }
    1113             groovyFile = groovyFile.replace('.', '/') + ".groovy";
    1114
    1115             //System.out.println("Attempting to load: " + groovyFile);
    1116             URL url = theClass.getClassLoader().getResource(groovyFile);
    1117             if (url == null) {
    1118                 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
    1119             }
    1120             if (url != null) {
    1121                 try {
    1122
    1123                     /***
    1124                      * todo there is no CompileUnit in scope so class name
    1125                      * checking won't work but that mostly affects the bytecode
    1126                      * generation rather than viewing the AST
    1127                      */
    1128
    1129                     CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
    1130                         public void call( ClassVisitor writer, ClassNode node ) {
    1131                             if( node.getName().equals(theClass.getName()) ) {
    1132                                 MetaClass.this.classNode = node;
    1133                             }
    1134                         }
    1135                     };
    1136
    1137
    1138                     CompilationUnit unit = new CompilationUnit( getClass().getClassLoader() );
    1139                     unit.setClassgenCallback( search );
    1140                     unit.addSource( url );
    1141                     unit.compile( Phases.CLASS_GENERATION );
    1142                 }
    1143                 catch (Exception e) {
    1144                     throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
    1145                 }
    1146             }
    1147
    1148         }
    1149         return classNode;
    1150     }
    1151
    1152     public String toString() {
    1153         return super.toString() + "[" + theClass + "]";
    1154     }
    1155
    1156     // Implementation methods
    1157     //-------------------------------------------------------------------------
    1158
    1159     /***
    1160      * Converts the given object into an array; if its an array then just cast
    1161      * otherwise wrap it in an array
    1162      */
    1163     protected Object[] asArray(Object arguments) {
    1164         if (arguments == null) {
    1165             return EMPTY_ARRAY;
    1166         }
    1167         if (arguments instanceof Tuple) {
    1168             Tuple tuple = (Tuple) arguments;
    1169             return tuple.toArray();
    1170         }
    1171         if (arguments instanceof Object[]) {
    1172             return (Object[]) arguments;
    1173         }
    1174         else {
    1175             return new Object[] { arguments };
    1176         }
    1177     }
    1178
    1179     /***
    1180      * @param listenerType
    1181      *            the interface of the listener to proxy
    1182      * @param listenerMethodName
    1183      *            the name of the method in the listener API to call the
    1184      *            closure on
    1185      * @param closure
    1186      *            the closure to invoke on the listenerMethodName method
    1187      *            invocation
    1188      * @return a dynamic proxy which calls the given closure on the given
    1189      *         method name
    1190      */
    1191     protected Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
    1192         InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
    1193         return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
    1194     }
    1195
    1196     /***
    1197      * Adds all the methods declared in the given class to the metaclass
    1198      * ignoring any matching methods already defined by a derived class
    1199      *
    1200      * @param theClass
    1201      */
    1202     protected void addMethods(Class theClass) {
    1203         Method[] methodArray = theClass.getDeclaredMethods();
    1204         for (int i = 0; i < methodArray.length; i++) {
    1205             Method reflectionMethod = methodArray[i];
    1206             if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
    1207                 continue;
    1208             }
    1209             MetaMethod method = createMetaMethod(reflectionMethod);
    1210             addMethod(method);
    1211         }
    1212     }
    1213
    1214     protected void addMethod(MetaMethod method) {
    1215         String name = method.getName();
    1216
    1217         //System.out.println(theClass.getName() + " == " + name + Arrays.asList(method.getParameterTypes()));
    1218
    1219         if (isGenericGetMethod(method) && genericGetMethod == null) {
    1220             genericGetMethod = method;
    1221         }
    1222         else if (isGenericSetMethod(method) && genericSetMethod == null) {
    1223             genericSetMethod = method;
    1224         }
    1225         if (method.isStatic()) {
    1226             List list = (List) staticMethodIndex.get(name);
    1227             if (list == null) {
    1228                 list = new ArrayList();
    1229                 staticMethodIndex.put(name, list);
    1230                 list.add(method);
    1231             }
    1232             else {
    1233                 if (!containsMatchingMethod(list, method)) {
    1234                     list.add(method);
    1235                 }
    1236             }
    1237         }
    1238
    1239         List list = (List) methodIndex.get(name);
    1240         if (list == null) {
    1241             list = new ArrayList();
    1242             methodIndex.put(name, list);
    1243             list.add(method);
    1244         }
    1245         else {
    1246             if (!containsMatchingMethod(list, method)) {
    1247                 list.add(method);
    1248             }
    1249         }
    1250     }
    1251
    1252     /***
    1253      * @return true if a method of the same matching prototype was found in the
    1254      *         list
    1255      */
    1256     protected boolean containsMatchingMethod(List list, MetaMethod method) {
    1257         for (Iterator iter = list.iterator(); iter.hasNext();) {
    1258             MetaMethod aMethod = (MetaMethod) iter.next();
    1259             Class[] params1 = aMethod.getParameterTypes();
    1260             Class[] params2 = method.getParameterTypes();
    1261             if (params1.length == params2.length) {
    1262                 boolean matches = true;
    1263                 for (int i = 0; i < params1.length; i++) {
    1264                     if (params1[i] != params2[i]) {
    1265                         matches = false;
    1266                         break;
    1267                     }
    1268                 }
    1269                 if (matches) {
    1270                     return true;
    1271                 }
    1272             }
    1273         }
    1274         return false;
    1275     }
    1276
    1277     /***
    1278      * Adds all of the newly defined methods from the given class to this
    1279      * metaclass
    1280      *
    1281      * @param theClass
    1282      */
    1283     protected void addNewStaticMethodsFrom(Class theClass) {
    1284         MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
    1285         Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
    1286         while (iter.hasNext()) {
    1287             MetaMethod method = (MetaMethod) iter.next();
    1288             addMethod(method);
    1289             newGroovyMethodsList.add(method);
    1290         }
    1291     }
    1292
    1293     /***
    1294      * @return the value of the static property of the given class
    1295      */
    1296     protected Object getStaticProperty(Class aClass, String property) {
    1297         //System.out.println("Invoking property: " + property + " on class: "
    1298         // + aClass);
    1299
    1300         Exception lastException = null;
    1301         try {
    1302             Field field = aClass.getField(property);
    1303             if (field != null) {
    1304                 if ((field.getModifiers() & Modifier.STATIC) != 0) {
    1305                     return field.get(null);
    1306                 }
    1307             }
    1308         }
    1309         catch (Exception e) {
    1310             lastException = e;
    1311         }
    1312
    1313         // lets try invoke a static getter method
    1314         try {
    1315             MetaMethod method = findStaticGetter(aClass, "get" + capitalize(property));
    1316             if (method != null) {
    1317                 return doMethodInvoke(aClass, method, EMPTY_ARRAY);
    1318             }
    1319         }
    1320         catch (GroovyRuntimeException e) {
    1321             throw new MissingPropertyException(property, aClass, e);
    1322         }
    1323
    1324         if (lastException == null) {
    1325             throw new MissingPropertyException(property, aClass);
    1326         }
    1327         else {
    1328             throw new MissingPropertyException(property, aClass, lastException);
    1329         }
    1330     }
    1331
    1332     /***
    1333      * @return the matching method which should be found
    1334      */
    1335     protected MetaMethod findMethod(Method aMethod) {
    1336         List methods = getMethods(aMethod.getName());
    1337         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1338             MetaMethod method = (MetaMethod) iter.next();
    1339             if (method.isMethod(aMethod)) {
    1340                 return method;
    1341             }
    1342         }
    1343         //log.warning("Creating reflection based dispatcher for: " + aMethod);
    1344         return new ReflectionMetaMethod(aMethod);
    1345     }
    1346
    1347     /***
    1348      * @return the getter method for the given object
    1349      */
    1350     protected MetaMethod findGetter(Object object, String name) {
    1351         List methods = getMethods(name);
    1352         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1353             MetaMethod method = (MetaMethod) iter.next();
    1354             if (method.getParameterTypes().length == 0) {
    1355                 return method;
    1356             }
    1357         }
    1358         return null;
    1359     }
    1360
    1361     /***
    1362      * @return the Method of the given name with no parameters or null
    1363      */
    1364     protected MetaMethod findStaticGetter(Class type, String name) {
    1365         List methods = getStaticMethods(name);
    1366         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1367             MetaMethod method = (MetaMethod) iter.next();
    1368             if (method.getParameterTypes().length == 0) {
    1369                 return method;
    1370             }
    1371         }
    1372
    1373         /*** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
    1374         try {
    1375             Method method = type.getMethod(name, EMPTY_TYPE_ARRAY);
    1376             if ((method.getModifiers() & Modifier.STATIC) != 0) {
    1377                 return findMethod(method);
    1378             }
    1379             else {
    1380                 return null;
    1381             }
    1382         }
    1383         catch (Exception e) {
    1384             return null;
    1385         }
    1386     }
    1387
    1388     protected Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
    1389         //System.out.println("Evaluating method: " + method);
    1390         //System.out.println("on object: " + object + " with arguments: " +
    1391         // InvokerHelper.toString(argumentArray));
    1392         //System.out.println(this.theClass);
    1393
    1394         try {
    1395             if (argumentArray == null) {
    1396                 argumentArray = EMPTY_ARRAY;
    1397             }
    1398             else if (method.getParameterTypes().length == 1 && argumentArray.length == 0) {
    1399                 argumentArray = ARRAY_WITH_NULL;
    1400             }
    1401             return method.invoke(object, argumentArray);
    1402         }
    1403         catch (ClassCastException e) {
    1404             if (coerceGStrings(argumentArray)) {
    1405                 try {
    1406                     return doMethodInvoke(object, method, argumentArray);
    1407                 }
    1408                 catch (Exception e2) {
    1409                     // allow fall through
    1410                 }
    1411             }
    1412             throw new GroovyRuntimeException(
    1413                 "failed to invoke method: "
    1414                     + method
    1415                     + " on: "
    1416                     + object
    1417                     + " with arguments: "
    1418                     + InvokerHelper.toString(argumentArray)
    1419                     + " reason: "
    1420                     + e,
    1421                 e);
    1422         }
    1423         catch (InvocationTargetException e) {
    1424             Throwable t = e.getTargetException();
    1425             if (t instanceof Error) {
    1426                 Error error = (Error) t;
    1427                 throw error;
    1428             }
    1429             if (t instanceof RuntimeException) {
    1430                 RuntimeException runtimeEx = (RuntimeException) t;
    1431                 throw runtimeEx;
    1432             }
    1433             throw new InvokerInvocationException(e);
    1434         }
    1435         catch (IllegalAccessException e) {
    1436             throw new GroovyRuntimeException(
    1437                 "could not access method: "
    1438                     + method
    1439                     + " on: "
    1440                     + object
    1441                     + " with arguments: "
    1442                     + InvokerHelper.toString(argumentArray)
    1443                     + " reason: "
    1444                     + e,
    1445                 e);
    1446         }
    1447         catch (IllegalArgumentException e) {
    1448             if (coerceGStrings(argumentArray)) {
    1449                 try {
    1450                     return doMethodInvoke(object, method, argumentArray);
    1451                 }
    1452                 catch (Exception e2) {
    1453                     // allow fall through
    1454                 }
    1455             }
    1456             Object[] args = coerceNumbers(method, argumentArray);
    1457             if (args != null) {
    1458                 try {
    1459                     return doMethodInvoke(object, method, args);
    1460                 }
    1461                 catch (Exception e3) {
    1462                     // allow fall through
    1463                 }
    1464             }
    1465
    1466             throw new GroovyRuntimeException(
    1467                     "failed to invoke method: "
    1468                     + method
    1469                     + " on: "
    1470                     + object
    1471                     + " with arguments: "
    1472                     + InvokerHelper.toString(argumentArray)
    1473                     + " reason: "
    1474                     + e,
    1475                     e);
    1476         }
    1477         catch (RuntimeException e) {
    1478             throw e;
    1479         }
    1480         catch (Exception e) {
    1481             throw new GroovyRuntimeException(
    1482                 "failed to invoke method: "
    1483                     + method
    1484                     + " on: "
    1485                     + object
    1486                     + " with arguments: "
    1487                     + InvokerHelper.toString(argumentArray)
    1488                     + " reason: "
    1489                     + e,
    1490                 e);
    1491         }
    1492     }
    1493
    1494     private static Object[] coerceNumbers(MetaMethod method, Object[] arguments) {
    1495         Object[] ans = null;
    1496         boolean coerced = false; // to indicate that at least one param is coerced
    1497
    1498         Class[] params = method.getParameterTypes();
    1499
    1500         if (params.length != arguments.length) {
    1501             return null;
    1502         }
    1503
    1504         ans = new Object[arguments.length];
    1505
    1506         for (int i = 0, size = arguments.length; i < size; i++) {
    1507             Object argument = arguments[i];
    1508             Class param = params[i];
    1509             if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) { // Number types
    1510                 if (param == Byte.class || param == Byte.TYPE ) {
    1511                     ans[i] = new Byte(((Number)argument).byteValue());
    1512                     coerced = true; continue;
    1513                 }
    1514                 if (param == Double.class || param == Double.TYPE) {
    1515                     ans[i] = new Double(((Number)argument).doubleValue());
    1516                     coerced = true; continue;
    1517                 }
    1518                 if (param == Float.class || param == Float.TYPE) {
    1519                     ans[i] = new Float(((Number)argument).floatValue());
    1520                     coerced = true; continue;
    1521                 }
    1522                 if (param == Integer.class || param == Integer.TYPE) {
    1523                     ans[i] = new Integer(((Number)argument).intValue());
    1524                     coerced = true; continue;
    1525                 }
    1526                 if (param == Long.class || param == Long.TYPE) {
    1527                     ans[i] = new Long(((Number)argument).longValue());
    1528                     coerced = true; continue;
    1529                 }
    1530                 if (param == Short.class || param == Short.TYPE) {
    1531                     ans[i] = new Short(((Number)argument).shortValue());
    1532                     coerced = true; continue;
    1533                 }
    1534                 if (param == BigDecimal.class ) {
    1535                     ans[i] = new BigDecimal(((Number)argument).doubleValue());
    1536                     coerced = true; continue;
    1537                 }
    1538                 if (param == BigInteger.class) {
    1539                     ans[i] = new BigInteger(String.valueOf(((Number)argument).longValue()));
    1540                     coerced = true; continue;
    1541                 }
    1542             }
    1543             else if (param.isArray() && argument.getClass().isArray()) {
    1544                 Class paramElem = param.getComponentType();
    1545                 if (paramElem.isPrimitive()) {
    1546                     if (paramElem == boolean.class && argument.getClass().getName().equals("[Ljava.lang.Boolean;")) {
    1547                         ans[i] = InvokerHelper.convertToBooleanArray(argument);
    1548                         coerced = true;
    1549                         continue;
    1550                     }
    1551                     if (paramElem == byte.class && argument.getClass().getName().equals("[Ljava.lang.Byte;")) {
    1552                         ans[i] = InvokerHelper.convertToByteArray(argument);
    1553                         coerced = true;
    1554                         continue;
    1555                     }
    1556                     if (paramElem == char.class && argument.getClass().getName().equals("[Ljava.lang.Character;")) {
    1557                         ans[i] = InvokerHelper.convertToCharArray(argument);
    1558                         coerced = true;
    1559                         continue;
    1560                     }
    1561                     if (paramElem == short.class && argument.getClass().getName().equals("[Ljava.lang.Short;")) {
    1562                         ans[i] = InvokerHelper.convertToShortArray(argument);
    1563                         coerced = true;
    1564                         continue;
    1565                     }
    1566                     if (paramElem == int.class && argument.getClass().getName().equals("[Ljava.lang.Integer;")) {
    1567                         ans[i] = InvokerHelper.convertToIntArray(argument);
    1568                         coerced = true;
    1569                         continue;
    1570                     }
    1571                     if (paramElem == long.class
    1572                             && argument.getClass().getName().equals("[Ljava.lang.Long;")
    1573                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
    1574                                                             ) {
    1575                         ans[i] = InvokerHelper.convertToLongArray(argument);
    1576                         coerced = true;
    1577                         continue;
    1578                     }
    1579                     if (paramElem == float.class
    1580                             && argument.getClass().getName().equals("[Ljava.lang.Float;")
    1581                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
    1582                                                             ) {
    1583                         ans[i] = InvokerHelper.convertToFloatArray(argument);
    1584                         coerced = true;
    1585                         continue;
    1586                     }
    1587                     if (paramElem == double.class &&
    1588                             argument.getClass().getName().equals("[Ljava.lang.Double;") &&
    1589                             argument.getClass().getName().equals("[Ljava.lang.BigDecimal;") &&
    1590                             argument.getClass().getName().equals("[Ljava.lang.Float;")) {
    1591                         ans[i] = InvokerHelper.convertToDoubleArray(argument);
    1592                         coerced = true;
    1593                         continue;
    1594                     }
    1595                 }
    1596             }
    1597         }
    1598         return coerced ? ans : null;
    1599     }
    1600
    1601     protected Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
    1602         //System.out.println("Evaluating constructor: " + constructor + " with
    1603         // arguments: " + InvokerHelper.toString(argumentArray));
    1604         //System.out.println(this.theClass);
    1605
    1606         try {
    1607 			// the following patch was provided by Mori Kouhei to fix JIRA 435
    1608 			/* but it opens the ctor up to everyone, so it is no longer private!
    1609             final Constructor ctor = constructor;
    1610             AccessController.doPrivileged(new PrivilegedAction() {
    1611                 public Object run() {
    1612                     ctor.setAccessible(ctor.getDeclaringClass().equals(theClass));
    1613                     return null;
    1614                 }
    1615             });
    1616 			*/
    1617 			// end of patch
    1618
    1619             return constructor.newInstance(argumentArray);
    1620         }
    1621         catch (InvocationTargetException e) {
    1622             Throwable t = e.getTargetException();
    1623             if (t instanceof Error) {
    1624                 Error error = (Error) t;
    1625                 throw error;
    1626             }
    1627             if (t instanceof RuntimeException) {
    1628                 RuntimeException runtimeEx = (RuntimeException) t;
    1629                 throw runtimeEx;
    1630             }
    1631             throw new InvokerInvocationException(e);
    1632         }
    1633         catch (IllegalArgumentException e) {
    1634             if (coerceGStrings(argumentArray)) {
    1635                 try {
    1636                     return constructor.newInstance(argumentArray);
    1637                 }
    1638                 catch (Exception e2) {
    1639                     // allow fall through
    1640                 }
    1641             }
    1642             throw new GroovyRuntimeException(
    1643                 "failed to invoke constructor: "
    1644                     + constructor
    1645                     + " with arguments: "
    1646                     + InvokerHelper.toString(argumentArray)
    1647                     + " reason: "
    1648                     + e,
    1649                 e);
    1650         }
    1651         catch (IllegalAccessException e) {
    1652             throw new GroovyRuntimeException(
    1653                 "could not access constructor: "
    1654                     + constructor
    1655                     + " with arguments: "
    1656                     + InvokerHelper.toString(argumentArray)
    1657                     + " reason: "
    1658                     + e,
    1659                 e);
    1660         }
    1661         catch (Exception e) {
    1662             throw new GroovyRuntimeException(
    1663                 "failed to invoke constructor: "
    1664                     + constructor
    1665                     + " with arguments: "
    1666                     + InvokerHelper.toString(argumentArray)
    1667                     + " reason: "
    1668                     + e,
    1669                 e);
    1670         }
    1671     }
    1672
    1673     /***
    1674      * Chooses the correct method to use from a list of methods which match by
    1675      * name.
    1676      *
    1677      * @param methods
    1678      *            the possible methods to choose from
    1679      * @param arguments
    1680      *            the original argument to the method
    1681      * @return
    1682      */
    1683     protected Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
    1684         int methodCount = methods.size();
    1685         if (methodCount <= 0) {
    1686             return null;
    1687         }
    1688         else if (methodCount == 1) {
    1689             Object method = methods.get(0);
    1690             if (isValidMethod(method, arguments, coerce)) {
    1691                 return method;
    1692             }
    1693             return null;
    1694         }
    1695         Object answer = null;
    1696         if (arguments == null || arguments.length == 0) {
    1697             answer = chooseEmptyMethodParams(methods);
    1698         }
    1699         else if (arguments.length == 1 && arguments[0] == null) {
    1700             answer = chooseMostGeneralMethodWith1NullParam(methods);
    1701         }
    1702         else {
    1703             List matchingMethods = new ArrayList();
    1704
    1705             for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1706                 Object method = iter.next();
    1707                 Class[] paramTypes;
    1708
    1709                 // making this false helps find matches
    1710                 if (isValidMethod(method, arguments, coerce)) {
    1711                     matchingMethods.add(method);
    1712                 }
    1713             }
    1714             if (matchingMethods.isEmpty()) {
    1715                 return null;
    1716             }
    1717             else if (matchingMethods.size() == 1) {
    1718                 return matchingMethods.get(0);
    1719             }
    1720             return chooseMostSpecificParams(methodName, matchingMethods, arguments);
    1721
    1722         }
    1723         if (answer != null) {
    1724             return answer;
    1725         }
    1726         throw new GroovyRuntimeException(
    1727             "Could not find which method to invoke from this list: "
    1728                 + methods
    1729                 + " for arguments: "
    1730                 + InvokerHelper.toString(arguments));
    1731     }
    1732
    1733     protected boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
    1734         Class[] paramTypes = getParameterTypes(method);
    1735         return isValidMethod(paramTypes, arguments, includeCoerce);
    1736     }
    1737
    1738     public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
    1739         if (arguments == null) {
    1740             return true;
    1741         }
    1742         int size = arguments.length;
    1743         boolean validMethod = false;
    1744         if (paramTypes.length == size) {
    1745             // lets check the parameter types match
    1746             validMethod = true;
    1747             for (int i = 0; i < size; i++) {
    1748                 if (!isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) {
    1749                     validMethod = false;
    1750                 }
    1751             }
    1752         }
    1753         else {
    1754             if (paramTypes.length == 1 && size == 0) {
    1755                 return true;
    1756             }
    1757         }
    1758         return validMethod;
    1759     }
    1760
    1761     protected Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
    1762         for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
    1763             Object method = iter.next();
    1764             Class[] paramTypes = getParameterTypes(method);
    1765             if (Arrays.equals(arguments, paramTypes)) return method;
    1766         }
    1767         Object answer = null;
    1768         int size = arguments.length;
    1769         Class[] mostSpecificTypes = null;
    1770         for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
    1771             Object method = iter.next();
    1772             Class[] paramTypes = getParameterTypes(method);
    1773             if (answer == null) {
    1774                 answer = method;
    1775                 mostSpecificTypes = paramTypes;
    1776             }
    1777             else {
    1778                 boolean useThisMethod = false;
    1779                 for (int i = 0; i < size; i++) {
    1780                     Class mostSpecificType = mostSpecificTypes[i];
    1781                     Class type = paramTypes[i];
    1782
    1783                     if (!isAssignableFrom(mostSpecificType, type)) {
    1784
    1785                         useThisMethod = true;
    1786                         break;
    1787                     }
    1788
    1789                 }
    1790                 if (useThisMethod) {
    1791
    1792                     if (size > 1) {
    1793                         checkForInvalidOverloading(name, mostSpecificTypes, paramTypes);
    1794                     }
    1795
    1796                     answer = method;
    1797                     mostSpecificTypes = paramTypes;
    1798                 }
    1799             }
    1800         }
    1801         return answer;
    1802     }
    1803
    1804     /***
    1805      * Checks that one of the parameter types is a superset of the other and
    1806      * that the two lists of types don't conflict. e.g. foo(String, Object) and
    1807      * foo(Object, String) would conflict if called with foo("a", "b").
    1808      *
    1809      * Note that this method is only called with 2 possible signatures. i.e.
    1810      * possible invalid combinations will already have been filtered out. So if
    1811      * there were methods foo(String, Object) and foo(Object, String) then one
    1812      * of these would be already filtered out if foo was called as foo(12, "a")
    1813      */
    1814     protected void checkForInvalidOverloading(String name, Class[] baseTypes, Class[] derivedTypes) {
    1815         for (int i = 0, size = baseTypes.length; i < size; i++) {
    1816             Class baseType = baseTypes[i];
    1817             Class derivedType = derivedTypes[i];
    1818             if (!isAssignableFrom(derivedType, baseType)) {
    1819                 throw new GroovyRuntimeException(
    1820                     "Ambiguous method overloading for method: "
    1821                         + name
    1822                         + ". Cannot resolve which method to invoke due to overlapping prototypes between: "
    1823                         + InvokerHelper.toString(baseTypes)
    1824                         + " and: "
    1825                         + InvokerHelper.toString(derivedTypes));
    1826             }
    1827         }
    1828     }
    1829
    1830     protected Class[] getParameterTypes(Object methodOrConstructor) {
    1831         if (methodOrConstructor instanceof MetaMethod) {
    1832             MetaMethod method = (MetaMethod) methodOrConstructor;
    1833             return method.getParameterTypes();
    1834         }
    1835         if (methodOrConstructor instanceof Method) {
    1836             Method method = (Method) methodOrConstructor;
    1837             return method.getParameterTypes();
    1838         }
    1839         if (methodOrConstructor instanceof Constructor) {
    1840             Constructor constructor = (Constructor) methodOrConstructor;
    1841             return constructor.getParameterTypes();
    1842         }
    1843         throw new IllegalArgumentException("Must be a Method or Constructor");
    1844     }
    1845
    1846     /***
    1847      * @return the method with 1 parameter which takes the most general type of
    1848      *         object (e.g. Object) ignoring primitve types
    1849      */
    1850     protected Object chooseMostGeneralMethodWith1NullParam(List methods) {
    1851         // lets look for methods with 1 argument which matches the type of the
    1852         // arguments
    1853         Class closestClass = null;
    1854         Object answer = null;
    1855
    1856         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1857             Object method = iter.next();
    1858             Class[] paramTypes = getParameterTypes(method);
    1859             int paramLength = paramTypes.length;
    1860             if (paramLength == 1) {
    1861                 Class theType = paramTypes[0];
    1862 				if (theType.isPrimitive()) continue;
    1863                 if (closestClass == null || isAssignableFrom(closestClass, theType)) {
    1864                     closestClass = theType;
    1865                     answer = method;
    1866                 }
    1867             }
    1868         }
    1869         return answer;
    1870     }
    1871
    1872     /***
    1873      * @return the method with 1 parameter which takes the most general type of
    1874      *         object (e.g. Object)
    1875      */
    1876     protected Object chooseEmptyMethodParams(List methods) {
    1877         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1878             Object method = iter.next();
    1879             Class[] paramTypes = getParameterTypes(method);
    1880             int paramLength = paramTypes.length;
    1881             if (paramLength == 0) {
    1882                 return method;
    1883             }
    1884         }
    1885         return null;
    1886     }
    1887
    1888     protected static boolean isCompatibleInstance(Class type, Object value, boolean includeCoerce) {
    1889         boolean answer = value == null || type.isInstance(value);
    1890         if (!answer) {
    1891             if (type.isPrimitive()) {
    1892                 if (type == int.class) {
    1893                     return value instanceof Integer;
    1894                 }
    1895                 else if (type == double.class) {
    1896                     return value instanceof Double || value instanceof Float || value instanceof Integer || value instanceof BigDecimal;
    1897                 }
    1898                 else if (type == boolean.class) {
    1899                     return value instanceof Boolean;
    1900                 }
    1901                 else if (type == long.class) {
    1902                     return value instanceof Long || value instanceof Integer;
    1903                 }
    1904                 else if (type == float.class) {
    1905                     return value instanceof Float || value instanceof Integer;
    1906                 }
    1907                 else if (type == char.class) {
    1908                     return value instanceof Character;
    1909                 }
    1910                 else if (type == byte.class) {
    1911                     return value instanceof Byte;
    1912                 }
    1913                 else if (type == short.class) {
    1914                     return value instanceof Short;
    1915                 }
    1916             }
    1917             else if(type.isArray() && value.getClass().isArray()) {
    1918                 return isCompatibleClass(type.getComponentType(), value.getClass().getComponentType(), false);
    1919             }
    1920             else if (includeCoerce) {
    1921                 if (type == String.class && value instanceof GString) {
    1922                     return true;
    1923                 }
    1924                 else if (value instanceof Number) {
    1925                     // lets allow numbers to be coerced downwards?
    1926                     return Number.class.isAssignableFrom(type);
    1927                 }
    1928             }
    1929         }
    1930         return answer;
    1931     }
    1932     protected static boolean isCompatibleClass(Class type, Class value, boolean includeCoerce) {
    1933         boolean answer = value == null || type.isAssignableFrom(value); // this might have taken care of primitive types, rendering part of the following code unnecessary
    1934         if (!answer) {
    1935             if (type.isPrimitive()) {
    1936                 if (type == int.class) {
    1937                     return value == Integer.class;// || value == BigDecimal.class; //br added BigDecimal
    1938                 }
    1939                 else if (type == double.class) {
    1940                     return value == Double.class || value == Float.class || value == Integer.class || value == BigDecimal.class;
    1941                 }
    1942                 else if (type == boolean.class) {
    1943                     return value == Boolean.class;
    1944                 }
    1945                 else if (type == long.class) {
    1946                     return value == Long.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
    1947                 }
    1948                 else if (type == float.class) {
    1949                     return value == Float.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
    1950                 }
    1951                 else if (type == char.class) {
    1952                     return value == Character.class;
    1953                 }
    1954                 else if (type == byte.class) {
    1955                     return value == Byte.class;
    1956                 }
    1957                 else if (type == short.class) {
    1958                     return value == Short.class;
    1959                 }
    1960             }
    1961             else if(type.isArray() && value.isArray()) {
    1962                 return isCompatibleClass(type.getComponentType(), value.getComponentType(), false);
    1963             }
    1964             else if (includeCoerce) {
    1965 //if (type == String.class && value == GString.class) {
    1966                 if (type == String.class && GString.class.isAssignableFrom(value)) {
    1967                     return true;
    1968                 }
    1969                 else if (value == Number.class) {
    1970                     // lets allow numbers to be coerced downwards?
    1971                     return Number.class.isAssignableFrom(type);
    1972                 }
    1973             }
    1974         }
    1975         return answer;
    1976     }
    1977
    1978     protected boolean isAssignableFrom(Class mostSpecificType, Class type) {
    1979         // let's handle primitives
    1980         if (mostSpecificType.isPrimitive() && type.isPrimitive()) {
    1981             if (mostSpecificType == type) {
    1982                 return true;
    1983             }
    1984             else {  // note: there is not coercion for boolean and char. Range matters, precision doesn't
    1985                 if (type == int.class) {
    1986                     return
    1987                             mostSpecificType == int.class
    1988                             || mostSpecificType == short.class
    1989                             || mostSpecificType == byte.class;
    1990                 }
    1991                 else if (type == double.class) {
    1992                     return
    1993                             mostSpecificType == double.class
    1994                             || mostSpecificType == int.class
    1995                             || mostSpecificType == long.class
    1996                             || mostSpecificType == short.class
    1997                             || mostSpecificType == byte.class
    1998                             || mostSpecificType == float.class;
    1999                 }
    2000                 else if (type == long.class) {
    2001                     return
    2002                             mostSpecificType == long.class
    2003                             || mostSpecificType == int.class
    2004                             || mostSpecificType == short.class
    2005                             || mostSpecificType == byte.class;
    2006                 }
    2007                 else if (type == float.class) {
    2008                     return
    2009                             mostSpecificType == float.class
    2010                             || mostSpecificType == int.class
    2011                             || mostSpecificType == long.class
    2012                             || mostSpecificType == short.class
    2013                             || mostSpecificType == byte.class;
    2014                 }
    2015                 else if (type == short.class) {
    2016                     return
    2017                             mostSpecificType == short.class
    2018                             || mostSpecificType == byte.class;
    2019                 }
    2020                 else {
    2021                     return false;
    2022                 }
    2023             }
    2024         }
    2025
    2026         boolean answer = type.isAssignableFrom(mostSpecificType);
    2027         if (!answer) {
    2028             answer = autoboxType(type).isAssignableFrom(autoboxType(mostSpecificType));
    2029         }
    2030         return answer;
    2031     }
    2032
    2033     private Class autoboxType(Class type) {
    2034         if (type.isPrimitive()) {
    2035             if (type == int.class) {
    2036                 return Integer.class;
    2037             }
    2038             else if (type == double.class) {
    2039                 return Double.class;
    2040             }
    2041             else if (type == long.class) {
    2042                 return Long.class;
    2043             }
    2044             else if (type == boolean.class) {
    2045                 return Boolean.class;
    2046             }
    2047             else if (type == float.class) {
    2048                 return Float.class;
    2049             }
    2050             else if (type == char.class) {
    2051                 return Character.class;
    2052             }
    2053             else if (type == byte.class) {
    2054                 return Byte.class;
    2055             }
    2056             else if (type == short.class) {
    2057                 return Short.class;
    2058             }
    2059         }
    2060         return type;
    2061     }
    2062
    2063     /***
    2064      * Coerces any GString instances into Strings
    2065      *
    2066      * @return true if some coercion was done.
    2067      */
    2068     protected static boolean coerceGStrings(Object[] arguments) {
    2069         boolean coerced = false;
    2070         for (int i = 0, size = arguments.length; i < size; i++) {
    2071             Object argument = arguments[i];
    2072             if (argument instanceof GString) {
    2073                 arguments[i] = argument.toString();
    2074                 coerced = true;
    2075             }
    2076         }
    2077         return coerced;
    2078     }
    2079
    2080     protected boolean isGenericSetMethod(MetaMethod method) {
    2081         return (method.getName().equals("set"))
    2082             && method.getParameterTypes().length == 2;
    2083     }
    2084
    2085     protected boolean isGenericGetMethod(MetaMethod method) {
    2086         if (method.getName().equals("get")) {
    2087             Class[] parameterTypes = method.getParameterTypes();
    2088             return parameterTypes.length == 1 && parameterTypes[0] == String.class;
    2089         }
    2090         return false;
    2091     }
    2092
    2093     private void registerMethods(boolean instanceMethods) {
    2094         Method[] methods = theClass.getMethods();
    2095         for (int i = 0; i < methods.length; i++) {
    2096             Method method = methods[i];
    2097             if (MethodHelper.isStatic(method)) {
    2098                 Class[] paramTypes = method.getParameterTypes();
    2099                 if (paramTypes.length > 0) {
    2100                     Class owner = paramTypes[0];
    2101                     if (instanceMethods) {
    2102                         registry.lookup(owner).addNewInstanceMethod(method);
    2103                     } else {
    2104                         registry.lookup(owner).addNewStaticMethod(method);
    2105                     }
    2106                 }
    2107             }
    2108         }
    2109     }
    2110
    2111     protected void registerStaticMethods() {
    2112         registerMethods(false);
    2113     }
    2114
    2115     protected void registerInstanceMethods() {
    2116         registerMethods(true);
    2117     }
    2118
    2119     protected String capitalize(String property) {
    2120         return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
    2121     }
    2122
    2123     /***
    2124      * Call this method when any mutation method is called, such as adding a new
    2125      * method to this MetaClass so that any caching or bytecode generation can be
    2126      * regenerated.
    2127      */
    2128     protected synchronized void onMethodChange() {
    2129         reflector = null;
    2130     }
    2131
    2132     protected synchronized void checkInitialised() {
    2133         if (!initialised) {
    2134             initialised = true;
    2135             addInheritedMethods(theClass);
    2136         }
    2137         if (reflector == null) {
    2138             generateReflector();
    2139         }
    2140     }
    2141
    2142     protected MetaMethod createMetaMethod(final Method method) {
    2143         if (registry.useAccessible()) {
    2144             AccessController.doPrivileged(new PrivilegedAction() {
    2145                 public Object run() {
    2146                     method.setAccessible(true);
    2147                     return null;
    2148                 }
    2149             });
    2150         }
    2151         if (useReflection) {
    2152             //log.warning("Creating reflection based dispatcher for: " + method);
    2153             return new ReflectionMetaMethod(method);
    2154         }
    2155         MetaMethod answer = new MetaMethod(method);
    2156         if (isValidReflectorMethod(answer)) {
    2157             allMethods.add(answer);
    2158             answer.setMethodIndex(allMethods.size());
    2159         }
    2160         else {
    2161             //log.warning("Creating reflection based dispatcher for: " + method);
    2162             answer = new ReflectionMetaMethod(method);
    2163         }
    2164         return answer;
    2165     }
    2166
    2167     protected boolean isValidReflectorMethod(MetaMethod method) {
    2168         // We cannot use a reflector if the method is private, protected, or package accessible only.
    2169         if (!method.isPublic()) {
    2170             return false;
    2171         }
    2172         Class declaringClass = method.getDeclaringClass();
    2173         if (!Modifier.isPublic(declaringClass.getModifiers())) {
    2174             // lets see if this method is implemented on an interface
    2175             List list = getInterfaceMethods();
    2176             for (Iterator iter = list.iterator(); iter.hasNext();) {
    2177                 MetaMethod aMethod = (MetaMethod) iter.next();
    2178                 if (method.isSame(aMethod)) {
    2179                     method.setInterfaceClass(aMethod.getDeclaringClass());
    2180                     return true;
    2181                 }
    2182             }
    2183             /*** todo */
    2184             //log.warning("Cannot invoke method on protected/private class which isn't visible on an interface so must use reflection instead: " + method);
    2185             return false;
    2186         }
    2187         return true;
    2188     }
    2189
    2190     protected void generateReflector() {
    2191         reflector = loadReflector(allMethods);
    2192         if (reflector == null) {
    2193             throw new RuntimeException("Should have a reflector!");
    2194         }
    2195         // lets set the reflector on all the methods
    2196         for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
    2197             MetaMethod metaMethod = (MetaMethod) iter.next();
    2198             //System.out.println("Setting reflector for method: " + metaMethod + " with index: " + metaMethod.getMethodIndex());
    2199             metaMethod.setReflector(reflector);
    2200         }
    2201     }
    2202
    2203     protected Reflector loadReflector(List methods) {
    2204         ReflectorGenerator generator = new ReflectorGenerator(methods);
    2205         String className = theClass.getName();
    2206         String packagePrefix = "gjdk.";
    2207         /*
    2208         if (className.startsWith("java.")) {
    2209             packagePrefix = "gjdk.";
    2210         }
    2211         */
    2212         String name = packagePrefix + className + "_GroovyReflector";
    2213         if (theClass.isArray()) {
    2214             String componentName = theClass.getComponentType().getName();
    2215             /*
    2216             if (componentName.startsWith("java.")) {
    2217                 packagePrefix = "gjdk.";
    2218             }
    2219             */
    2220             name = packagePrefix + componentName + "_GroovyReflectorArray";
    2221         }
    2222         // lets see if its already loaded
    2223         try {
    2224             Class type = loadReflectorClass(name);
    2225             return (Reflector) type.newInstance();
    2226         }
    2227         catch (AccessControlException ace) {
    2228             //Don't ignore this exception type
    2229             throw ace;
    2230         }
    2231         catch (Exception e) {
    2232             // lets ignore, lets generate it && load it
    2233         }
    2234
    2235         ClassWriter cw = new ClassWriter(true);
    2236         generator.generate(cw, name);
    2237
    2238         byte[] bytecode = cw.toByteArray();
    2239
    2240         try {
    2241             Class type = loadReflectorClass(name, bytecode);
    2242             return (Reflector) type.newInstance();
    2243         }
    2244         catch (Exception e) {
    2245             throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + e, e);
    2246         }
    2247     }
    2248
    2249     protected Class loadReflectorClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
    2250         ClassLoader loader = theClass.getClassLoader();
    2251         if (loader instanceof GroovyClassLoader) {
    2252             final GroovyClassLoader gloader = (GroovyClassLoader) loader;
    2253             return (Class) AccessController.doPrivileged(new PrivilegedAction() {
    2254                 public Object run() {
    2255                     return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
    2256                 }
    2257             });
    2258         }
    2259         return registry.loadClass(name, bytecode);
    2260     }
    2261
    2262     protected Class loadReflectorClass(String name) throws ClassNotFoundException {
    2263         ClassLoader loader = theClass.getClassLoader();
    2264         if (loader instanceof GroovyClassLoader) {
    2265             GroovyClassLoader gloader = (GroovyClassLoader) loader;
    2266             return gloader.loadClass(name);
    2267         }
    2268         return registry.loadClass(name);
    2269     }
    2270
    2271     public List getMethods() {
    2272         return allMethods;
    2273     }
    2274
    2275     public List getMetaMethods() {
    2276         return (List) ((ArrayList)newGroovyMethodsList).clone();
    2277     }
    2278
    2279     protected synchronized List getInterfaceMethods() {
    2280         if (interfaceMethods == null) {
    2281             interfaceMethods = new ArrayList();
    2282             Class type = theClass;
    2283             while (type != null) {
    2284                 Class[] interfaces = type.getInterfaces();
    2285                 for (int i = 0; i < interfaces.length; i++) {
    2286                     Class iface = interfaces[i];
    2287                     Method[] methods = iface.getMethods();
    2288                     addInterfaceMethods(interfaceMethods, methods);
    2289                 }
    2290                 type = type.getSuperclass();
    2291             }
    2292         }
    2293         return interfaceMethods;
    2294     }
    2295
    2296     private void addInterfaceMethods(List list, Method[] methods) {
    2297         for (int i = 0; i < methods.length; i++) {
    2298             list.add(createMetaMethod(methods[i]));
    2299         }
    2300     }
    2301
    2302     /***
    2303      * param instance array to the type array
    2304      * @param args
    2305      * @return
    2306      */
    2307     Class[] convertToTypeArray(Object[] args) {
    2308         if (args == null)
    2309             return null;
    2310         int s = args.length;
    2311         Class[] ans = new Class[s];
    2312         for (int i = 0; i < s; i++) {
    2313             Object o = args[i];
    2314             if (o != null) {
    2315                 ans[i] = o.getClass();
    2316             } else {
    2317                 ans[i] = null;
    2318             }
    2319         }
    2320         return ans;
    2321     }
    2322 }