View Javadoc

    1   /*
    2   $Id: MetaClassImpl.java,v 1.18 2006/06/25 18:17:17 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.Method;
    57  import java.lang.reflect.Modifier;
    58  import java.net.URL;
    59  import java.security.AccessController;
    60  import java.security.PrivilegedAction;
    61  import java.security.PrivilegedActionException;
    62  import java.security.PrivilegedExceptionAction;
    63  import java.util.ArrayList;
    64  import java.util.Arrays;
    65  import java.util.Collection;
    66  import java.util.Collections;
    67  import java.util.HashMap;
    68  import java.util.Iterator;
    69  import java.util.LinkedList;
    70  import java.util.List;
    71  import java.util.Map;
    72  import java.util.logging.Level;
    73
    74  import org.codehaus.groovy.ast.ClassNode;
    75  import org.codehaus.groovy.classgen.ReflectorGenerator;
    76  import org.codehaus.groovy.control.CompilationUnit;
    77  import org.codehaus.groovy.control.CompilerConfiguration;
    78  import org.codehaus.groovy.control.Phases;
    79  import org.codehaus.groovy.runtime.CurriedClosure;
    80  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    81  import org.codehaus.groovy.runtime.GroovyCategorySupport;
    82  import org.codehaus.groovy.runtime.Invoker;
    83  import org.codehaus.groovy.runtime.InvokerHelper;
    84  import org.codehaus.groovy.runtime.MetaClassHelper;
    85  import org.codehaus.groovy.runtime.MethodClosure;
    86  import org.codehaus.groovy.runtime.MethodKey;
    87  import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
    88  import org.codehaus.groovy.runtime.NewStaticMetaMethod;
    89  import org.codehaus.groovy.runtime.ReflectionMetaMethod;
    90  import org.codehaus.groovy.runtime.Reflector;
    91  import org.codehaus.groovy.runtime.TemporaryMethodKey;
    92  import org.codehaus.groovy.runtime.TransformMetaMethod;
    93  import org.codehaus.groovy.runtime.wrappers.Wrapper;
    94  import org.objectweb.asm.ClassVisitor;
    95  import org.objectweb.asm.ClassWriter;
    96
    97  /***
    98  * Allows methods to be dynamically added to existing classes at runtime
    99  *
    100 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    101 * @author Guillaume Laforge
    102 * @author Jochen Theodorou
    103 * @version $Revision: 1.18 $
    104 */
    105 public class MetaClassImpl extends MetaClass {
    106
    107    protected MetaClassRegistry registry;
    108    private ClassNode classNode;
    109    private Map methodIndex = new HashMap();
    110    private Map staticMethodIndex = new HashMap();
    111    //private Map propertyDescriptors = Collections.synchronizedMap(new HashMap());
    112    private Map propertyMap = Collections.synchronizedMap(new HashMap());
    113    private Map listeners = new HashMap();
    114    private Map methodCache = Collections.synchronizedMap(new HashMap());
    115    private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
    116    private MetaMethod genericGetMethod;
    117    private MetaMethod genericSetMethod;
    118    private List constructors;
    119    private List allMethods = new ArrayList();
    120    private List interfaceMethods;
    121    private Reflector reflector;
    122    private boolean initialised;
    123    // we only need one of these that can be reused over and over.
    124    private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
    125    private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new Class[]{},null,0);
    126    private static final Object[] EMPTY_ARGUMENTS = {};
    127
    128    public MetaClassImpl(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
    129        super(theClass);
    130        this.registry = registry;
    131
    132        constructors = (List) AccessController.doPrivileged(new  PrivilegedAction() {
    133                public Object run() {
    134                    return Arrays.asList (theClass.getDeclaredConstructors());
    135                }
    136            });
    137
    138        addMethods(theClass,true);
    139
    140        // introspect
    141        BeanInfo info = null;
    142        try {
    143            info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
    144                public Object run() throws IntrospectionException {
    145                    return Introspector.getBeanInfo(theClass);
    146                }
    147            });
    148        } catch (PrivilegedActionException pae) {
    149            if (pae.getException() instanceof IntrospectionException) {
    150                throw (IntrospectionException) pae.getException();
    151            } else {
    152                throw new RuntimeException(pae.getException());
    153            }
    154        }
    155
    156        PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
    157
    158        // build up the metaproperties based on the public fields, property descriptors,
    159        // and the getters and setters
    160        setupProperties(descriptors);
    161
    162        /* old code
    163        for (int i = 0; i < descriptors.length; i++) {
    164            PropertyDescriptor descriptor = descriptors[i];
    165            propertyDescriptors.put(descriptor.getName(), descriptor);
    166        }
    167        */
    168
    169        EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
    170        for (int i = 0; i < eventDescriptors.length; i++) {
    171            EventSetDescriptor descriptor = eventDescriptors[i];
    172            Method[] listenerMethods = descriptor.getListenerMethods();
    173            for (int j = 0; j < listenerMethods.length; j++) {
    174                Method listenerMethod = listenerMethods[j];
    175                MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
    176                String name = listenerMethod.getName();
    177                if (listeners.containsKey(name)) {
    178                    listeners.put(name, AMBIGOUS_LISTENER_METHOD);
    179                } else{
    180                    listeners.put(name, metaMethod);
    181                }
    182            }
    183        }
    184    }
    185
    186    private void addInheritedMethods() {
    187        LinkedList superClasses = new LinkedList();
    188        for (Class c = theClass.getSuperclass(); c!=Object.class && c!= null; c = c.getSuperclass()) {
    189            superClasses.addFirst(c);
    190        }
    191        // lets add all the base class methods
    192        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    193            Class c = (Class) iter.next();
    194            addMethods(c,true);
    195            addNewStaticMethodsFrom(c);
    196        }
    197
    198        // now lets see if there are any methods on one of my interfaces
    199        Class[] interfaces = theClass.getInterfaces();
    200        for (int i = 0; i < interfaces.length; i++) {
    201            addNewStaticMethodsFrom(interfaces[i]);
    202        }
    203
    204        // lets add Object methods after interfaces, as all interfaces derive from Object.
    205        // this ensures List and Collection methods come before Object etc
    206        if (theClass != Object.class) {
    207            addMethods(Object.class, false);
    208            addNewStaticMethodsFrom(Object.class);
    209        }
    210
    211        if (theClass.isArray() && !theClass.equals(Object[].class)) {
    212            addNewStaticMethodsFrom(Object[].class);
    213        }
    214    }
    215
    216    /***
    217     * @return all the normal instance methods avaiable on this class for the
    218     *         given name
    219     */
    220    private List getMethods(String name) {
    221        List answer = (List) methodIndex.get(name);
    222        List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
    223        if (used != null) {
    224            if (answer != null) {
    225                used.addAll(answer);
    226            }
    227            answer = used;
    228        }
    229        if (answer == null) {
    230            answer = Collections.EMPTY_LIST;
    231        }
    232        return answer;
    233    }
    234
    235    /***
    236     * @return all the normal static methods avaiable on this class for the
    237     *         given name
    238     */
    239    private List getStaticMethods(String name) {
    240        List answer = (List) staticMethodIndex.get(name);
    241        if (answer == null) {
    242            return Collections.EMPTY_LIST;
    243        }
    244        return answer;
    245    }
    246
    247    /***
    248     * Allows static method definitions to be added to a meta class as if it
    249     * was an instance method
    250     *
    251     * @param method
    252     */
    253    public void addNewInstanceMethod(Method method) {
    254        if (initialised) {
    255            throw new RuntimeException("Already initialized, cannot add new method: " + method);
    256        }
    257        else {
    258            NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
    259            if (! newGroovyMethodsList.contains(newMethod)){
    260                newGroovyMethodsList.add(newMethod);
    261                addMethod(newMethod,false);
    262            }
    263        }
    264    }
    265
    266    public void addNewStaticMethod(Method method) {
    267        if (initialised) {
    268            throw new RuntimeException("Already initialized, cannot add new method: " + method);
    269        }
    270        else {
    271            NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
    272            if (! newGroovyMethodsList.contains(newMethod)){
    273                newGroovyMethodsList.add(newMethod);
    274                addMethod(newMethod,false);
    275            }
    276        }
    277    }
    278
    279    /***
    280     * Invokes the given method on the object.
    281     *
    282     */
    283    public Object invokeMethod(Object object, String methodName, Object[] arguments) {
    284        if (object == null) {
    285            throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
    286        }
    287        if (log.isLoggable(Level.FINER)){
    288            MetaClassHelper.logMethodCall(object, methodName, arguments);
    289        }
    290        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    291
    292        //
    293        // Temp code to ignore wrapped parameters
    294        // The New MOP will deal with these properly
    295        //
    296        for (int i = 0; i != arguments.length; i++) {
    297         if (arguments[i] instanceof Wrapper) {
    298           arguments[i] = ((Wrapper)arguments[i]).unwrap();
    299         }
    300        }
    301
    302        MetaMethod method = retrieveMethod(object, methodName, arguments);
    303
    304        boolean isClosure = object instanceof Closure;
    305        if (isClosure) {
    306            Closure closure = (Closure) object;
    307            Object delegate = closure.getDelegate();
    308            Object owner = closure.getOwner();
    309
    310            if ("call".equals(methodName) || "doCall".equals(methodName)) {
    311                if (object.getClass()==MethodClosure.class) {
    312                    MethodClosure mc = (MethodClosure) object;
    313                    methodName = mc.getMethod();
    314                    MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
    315                    return ownerMetaClass.invokeMethod(owner,methodName,arguments);
    316                } else if (object.getClass()==CurriedClosure.class) {
    317                    CurriedClosure cc = (CurriedClosure) object;
    318                    // change the arguments for an uncurried call
    319                    arguments = cc.getUncurriedArguments(arguments);
    320                    MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
    321                    return ownerMetaClass.invokeMethod(owner,methodName,arguments);
    322                }
    323            } else if ("curry".equals(methodName)) {
    324                return closure.curry(arguments);
    325            }
    326
    327            if (method==null && owner!=closure) {
    328                MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
    329                method = ownerMetaClass.retrieveMethod(owner,methodName,arguments);
    330                if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,arguments);
    331            }
    332            if (method==null && delegate!=closure && delegate!=null) {
    333                MetaClass delegateMetaClass = registry.getMetaClass(delegate.getClass());
    334                method = delegateMetaClass.retrieveMethod(delegate,methodName,arguments);
    335                if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,arguments);
    336            }
    337            if (method==null) {
    338                // still no methods found, test if delegate or owner are GroovyObjects
    339                // and invoke the method on them if so.
    340                MissingMethodException last = null;
    341                if (owner!=closure && (owner instanceof GroovyObject)) {
    342                    try {
    343                        GroovyObject go = (GroovyObject) owner;
    344                        return go.invokeMethod(methodName,arguments);
    345                    } catch (MissingMethodException mme) {
    346                        if (last==null) last = mme;
    347                    }
    348                }
    349                if (delegate!=closure && (delegate instanceof GroovyObject)) {
    350                    try {
    351                        GroovyObject go = (GroovyObject) delegate;
    352                        return go.invokeMethod(methodName,arguments);
    353                    } catch (MissingMethodException mme) {
    354                        last = mme;
    355                    }
    356                }
    357                if (last!=null) throw last;
    358            }
    359
    360        }
    361
    362        if (method != null) {
    363            return MetaClassHelper.doMethodInvoke(object, method, arguments);
    364        } else {
    365            // if no method was found, try to find a closure defined as a field of the class and run it
    366            try {
    367                Object value = this.getProperty(object, methodName);
    368                if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
    369                    Closure closure = (Closure) value;
    370                    MetaClass delegateMetaClass = registry.getMetaClass(closure.getClass());
    371                    return delegateMetaClass.invokeMethod(closure,"doCall",arguments);
    372                }
    373            } catch (MissingPropertyException mpe) {}
    374
    375            throw new MissingMethodException(methodName, theClass, arguments);
    376        }
    377    }
    378
    379    public MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
    380        // lets try use the cache to find the method
    381        MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    382        MetaMethod method = (MetaMethod) methodCache.get(methodKey);
    383        if (method == null) {
    384            method = pickMethod(owner, methodName, arguments);
    385            if (method != null && method.isCacheable()) {
    386                methodCache.put(methodKey.createCopy(), method);
    387            }
    388        }
    389        return method;
    390    }
    391
    392    public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
    393        // lets try use the cache to find the method
    394        MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    395        MetaMethod method = (MetaMethod) methodCache.get(methodKey);
    396        if (method == null) {
    397            method = pickMethod(methodName, arguments); // todo shall call pickStaticMethod also?
    398            if (method != null && method.isCacheable()) {
    399                methodCache.put(methodKey.createCopy(), method);
    400            }
    401        }
    402        return method;
    403    }
    404
    405    public Constructor retrieveConstructor(Class[] arguments) {
    406        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
    407        if (constructor != null) {
    408            return constructor;
    409        }
    410        else {
    411            constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
    412            if (constructor != null) {
    413                return constructor;
    414            }
    415        }
    416        return null;
    417    }
    418
    419    public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
    420        MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    421        MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    422        if (method == null) {
    423            method = pickStaticMethod(methodName, arguments);
    424            if (method != null) {
    425                staticMethodCache.put(methodKey.createCopy(), method);
    426            }
    427        }
    428        return method;
    429    }
    430    /***
    431     * Picks which method to invoke for the given object, method name and arguments
    432     */
    433    public MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
    434        MetaMethod method = null;
    435        List methods = getMethods(methodName);
    436        if (!methods.isEmpty()) {
    437            Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    438            method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
    439            if (method == null) {
    440                int size = (arguments != null) ? arguments.length : 0;
    441                if (size == 1) {
    442                    Object firstArgument = arguments[0];
    443                    if (firstArgument instanceof List) {
    444                        // lets coerce the list arguments into an array of
    445                        // arguments
    446                        // e.g. calling JFrame.setLocation( [100, 100] )
    447
    448                        List list = (List) firstArgument;
    449                        arguments = list.toArray();
    450                        argClasses = MetaClassHelper.convertToTypeArray(arguments);
    451                        method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
    452                        if (method==null) return null;
    453                            return new TransformMetaMethod(method) {
    454                                public Object invoke(Object object, Object[] arguments) throws Exception {
    455                                    Object firstArgument = arguments[0];
    456                                    List list = (List) firstArgument;
    457                                    arguments = list.toArray();
    458                                    return super.invoke(object, arguments);
    459                                }
    460                            };
    461                    }
    462                }
    463            }
    464        }
    465        return method;
    466    }
    467
    468    /***
    469     * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
    470     * this method is used only by ClassGenerator for static binding
    471     * @param methodName
    472     * @param arguments
    473     * @return
    474     */
    475    public MetaMethod pickMethod(String methodName, Class[] arguments) {
    476        MetaMethod method = null;
    477        List methods = getMethods(methodName);
    478        if (!methods.isEmpty()) {
    479            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    480 //no coersion at classgen time.
    481 //           if (method == null) {
    482 //               method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
    483 //           }
    484        }
    485        return method;
    486    }
    487
    488    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
    489        if (log.isLoggable(Level.FINER)){
    490            MetaClassHelper.logMethodCall(object, methodName, arguments);
    491        }
    492        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    493        // lets try use the cache to find the method
    494        MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
    495        MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    496        if (method == null) {
    497            method = pickStaticMethod(object, methodName, arguments);
    498            if (method != null) {
    499                staticMethodCache.put(methodKey.createCopy(), method);
    500            }
    501        }
    502
    503        if (method != null) {
    504            return MetaClassHelper.doMethodInvoke(object, method, arguments);
    505        }
    506
    507        throw new MissingMethodException(methodName, theClass, arguments);
    508    }
    509
    510    private MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
    511        MetaMethod method = null;
    512        List methods = getStaticMethods(methodName);
    513
    514        if (!methods.isEmpty()) {
    515            method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), false);
    516        }
    517
    518        if (method == null && theClass != Class.class) {
    519            MetaClass classMetaClass = registry.getMetaClass(Class.class);
    520            method = classMetaClass.pickMethod(object, methodName, arguments);
    521        }
    522        if (method == null) {
    523            method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
    524        }
    525        return method;
    526    }
    527
    528    private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
    529        MetaMethod method = null;
    530        List methods = getStaticMethods(methodName);
    531
    532        if (!methods.isEmpty()) {
    533            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    534 //disabled to keep consistant with the original version of pickStatciMethod
    535 //           if (method == null) {
    536 //               method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
    537 //           }
    538        }
    539
    540        if (method == null && theClass != Class.class) {
    541            MetaClass classMetaClass = registry.getMetaClass(Class.class);
    542            method = classMetaClass.pickMethod(methodName, arguments);
    543        }
    544        return method;
    545    }
    546
    547    public Object invokeConstructor(Object[] arguments) {
    548        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    549        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    550        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
    551        if (constructor != null) {
    552            return MetaClassHelper.doConstructorInvoke(constructor, arguments);
    553        }
    554        else {
    555            constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
    556            if (constructor != null) {
    557                return MetaClassHelper.doConstructorInvoke(constructor, arguments);
    558            }
    559        }
    560
    561        if (arguments.length == 1) {
    562            Object firstArgument = arguments[0];
    563            if (firstArgument instanceof Map) {
    564                constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
    565                if (constructor != null) {
    566                    Object bean = MetaClassHelper.doConstructorInvoke(constructor, MetaClassHelper.EMPTY_ARRAY);
    567                    setProperties(bean, ((Map) firstArgument));
    568                    return bean;
    569                }
    570            }
    571        }
    572        throw new GroovyRuntimeException(
    573                    "Could not find matching constructor for: "
    574                        + theClass.getName()
    575                        + "("+InvokerHelper.toTypeString(arguments)+")");
    576    }
    577
    578    public Object invokeConstructorAt(Class at, Object[] arguments) {
    579        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    580        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    581        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
    582        if (constructor != null) {
    583            return doConstructorInvokeAt(at, constructor, arguments);
    584        }
    585        else {
    586            constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
    587            if (constructor != null) {
    588                return doConstructorInvokeAt(at, constructor, arguments);
    589            }
    590        }
    591
    592        if (arguments.length == 1) {
    593            Object firstArgument = arguments[0];
    594            if (firstArgument instanceof Map) {
    595                constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
    596                if (constructor != null) {
    597                    Object bean = doConstructorInvokeAt(at, constructor, MetaClassHelper.EMPTY_ARRAY);
    598                    setProperties(bean, ((Map) firstArgument));
    599                    return bean;
    600                }
    601            }
    602        }
    603        throw new GroovyRuntimeException(
    604                    "Could not find matching constructor for: "
    605                        + theClass.getName()
    606                        + "("+InvokerHelper.toTypeString(arguments)+")");
    607    }
    608
    609    /***
    610     * Sets a number of bean properties from the given Map where the keys are
    611     * the String names of properties and the values are the values of the
    612     * properties to set
    613     */
    614    public void setProperties(Object bean, Map map) {
    615        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
    616            Map.Entry entry = (Map.Entry) iter.next();
    617            String key = entry.getKey().toString();
    618
    619            Object value = entry.getValue();
    620            try {
    621                setProperty(bean, key, value);
    622            }
    623            catch (GroovyRuntimeException e) {
    624                // lets ignore missing properties
    625                /*** todo should replace this code with a getMetaProperty(key) != null check
    626                 i.e. don't try and set a non-existent property
    627                 */
    628            }
    629        }
    630    }
    631
    632    /***
    633     * @return the given property's value on the object
    634     */
    635    public Object getProperty(final Object object, final String property) {
    636        // look for the property in our map
    637        MetaProperty mp = (MetaProperty) propertyMap.get(property);
    638        if (mp != null) {
    639            try {
    640                //System.out.println("we found a metaproperty for " + theClass.getName() +
    641                //  "." + property);
    642                // delegate the get operation to the metaproperty
    643                return mp.getProperty(object);
    644            }
    645            catch(Exception e) {
    646                throw new GroovyRuntimeException("Cannot read property: " + property);
    647            }
    648        }
    649
    650        if (genericGetMethod == null) {
    651            // Make sure there isn't a generic method in the "use" cases
    652            List possibleGenericMethods = getMethods("get");
    653            if (possibleGenericMethods != null) {
    654                for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
    655                    MetaMethod mmethod = (MetaMethod) i.next();
    656                    Class[] paramTypes = mmethod.getParameterTypes();
    657                    if (paramTypes.length == 1 && paramTypes[0] == String.class) {
    658                        Object[] arguments = {property};
    659                        Object answer = MetaClassHelper.doMethodInvoke(object, mmethod, arguments);
    660                        return answer;
    661                    }
    662                }
    663            }
    664        }
    665        else {
    666            Object[] arguments = { property };
    667            Object answer = MetaClassHelper.doMethodInvoke(object, genericGetMethod, arguments);
    668            // jes bug? a property retrieved via a generic get() can't have a null value?
    669            if (answer != null) {
    670                return answer;
    671            }
    672        }
    673
    674        if (!CompilerConfiguration.isJsrGroovy()) {
    675            // is the property the name of a method - in which case return a
    676            // closure
    677            List methods = getMethods(property);
    678            if (!methods.isEmpty()) {
    679                return new MethodClosure(object, property);
    680            }
    681        }
    682
    683        // lets try invoke a static getter method
    684        // this case is for protected fields. I wish there was a better way...
    685        Exception lastException = null;
    686        try {
    687            if ( !(object instanceof Class) ) {
    688                MetaMethod method = findGetter(object, "get" + MetaClassHelper.capitalize(property));
    689                if (method != null) {
    690                   return MetaClassHelper.doMethodInvoke(object, method, MetaClassHelper.EMPTY_ARRAY);
    691               }
    692           }
    693        }
    694        catch (GroovyRuntimeException e) {
    695            lastException = e;
    696        }
    697
    698        /*** todo or are we an extensible groovy class? */
    699        if (genericGetMethod != null) {
    700            return null;
    701        }
    702        else {
    703            /*** todo these special cases should be special MetaClasses maybe */
    704            if (object instanceof Class) {
    705                // lets try a static field
    706                return getStaticProperty((Class) object, property);
    707            }
    708            if (object instanceof Collection) {
    709                return DefaultGroovyMethods.getAt((Collection) object, property);
    710            }
    711            if (object instanceof Object[]) {
    712                return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
    713            }
    714            if (object instanceof Object) {
    715                try {
    716                    return getAttribute(object,property);
    717                } catch (MissingFieldException mfe) {
    718                    // do nothing
    719                }
    720            }
    721
    722            MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
    723            if (addListenerMethod != null) {
    724                /* @todo one day we could try return the previously registered Closure listener for easy removal */
    725                return null;
    726            }
    727
    728            if (lastException == null)
    729                throw new MissingPropertyException(property, theClass);
    730            else
    731                throw new MissingPropertyException(property, theClass, lastException);
    732        }
    733    }
    734
    735    /***
    736     * Get all the properties defined for this type
    737     * @return a list of MetaProperty objects
    738     */
    739    public List getProperties() {
    740        // simply return the values of the metaproperty map as a List
    741        return new ArrayList(propertyMap.values());
    742    }
    743
    744    /***
    745     * This will build up the property map (Map of MetaProperty objects, keyed on
    746     * property name).
    747     */
    748    private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
    749        MetaProperty mp;
    750        Method method;
    751        MetaMethod getter = null;
    752        MetaMethod setter = null;
    753        Class klass;
    754
    755        // first get the public fields and create MetaFieldProperty objects
    756        klass = theClass;
    757        while(klass != null) {
    758            final Class clazz = klass;
    759            Field[] fields = (Field[]) AccessController.doPrivileged(new  PrivilegedAction() {
    760                public Object run() {
    761                    return clazz.getDeclaredFields();
    762                }
    763            });
    764            for(int i = 0; i < fields.length; i++) {
    765                // todo: GROOVY-996
    766                // we're only interested in publics and protected
    767                if ((fields[i].getModifiers() & (java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.PROTECTED)) == 0)
    768                     continue;
    769
    770                // see if we already got this
    771                if(propertyMap.get(fields[i].getName()) != null)
    772                    continue;
    773
    774                //System.out.println("adding field " + fields[i].getName() +
    775                //  " for class " + klass.getName());
    776                // stick it in there!
    777                propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
    778            }
    779
    780            // now get the super class
    781            klass = klass.getSuperclass();
    782        }
    783
    784   // if this an Array, then add the special read-only "length" property
    785        if (theClass.isArray()) {
    786            propertyMap.put("length", arrayLengthProperty);
    787        }
    788
    789        // now iterate over the map of property descriptors and generate
    790        // MetaBeanProperty objects
    791        for(int i=0; i<propertyDescriptors.length; i++) {
    792            PropertyDescriptor pd = propertyDescriptors[i];
    793
    794            // skip if the property type is unknown (this seems to be the case if the
    795            // property descriptor is based on a setX() method that has two parameters,
    796            // which is not a valid property)
    797            if(pd.getPropertyType() == null)
    798                continue;
    799
    800            // get the getter method
    801            method = pd.getReadMethod();
    802            if(method != null)
    803                getter = findMethod(method);
    804            else
    805                getter = null;
    806
    807            // get the setter method
    808            method = pd.getWriteMethod();
    809            if(method != null)
    810                setter = findMethod(method);
    811            else
    812                setter = null;
    813
    814            // now create the MetaProperty object
    815            //System.out.println("creating a bean property for class " +
    816            //  theClass.getName() + ": " + pd.getName());
    817
    818            mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
    819
    820            // put it in the list
    821            // this will overwrite a possible field property
    822            propertyMap.put(pd.getName(), mp);
    823        }
    824
    825        // now look for any stray getters that may be used to define a property
    826        klass = theClass;
    827        while(klass != null) {
    828            final Class clazz = klass;
    829            Method[] methods = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
    830                public Object run() {
    831                    return clazz.getDeclaredMethods();
    832                }
    833            });
    834            for (int i = 0; i < methods.length; i++) {
    835                // filter out the privates
    836                if(Modifier.isPublic(methods[i].getModifiers()) == false)
    837                    continue;
    838
    839                method = methods[i];
    840
    841                String methodName = method.getName();
    842
    843                // is this a getter?
    844                if(methodName.startsWith("get") &&
    845                    methodName.length() > 3 &&
    846                    method.getParameterTypes().length == 0) {
    847
    848                    // get the name of the property
    849                    String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
    850
    851                    // is this property already accounted for?
    852                    mp = (MetaProperty) propertyMap.get(propName);
    853                    if(mp != null) {
    854                        // we may have already found the setter for this
    855                        if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
    856                            // update the getter method to this one
    857                            ((MetaBeanProperty) mp).setGetter(findMethod(method));
    858                        }
    859                    }
    860                    else {
    861                        // we need to create a new property object
    862                        // type of the property is what the get method returns
    863                        MetaBeanProperty mbp = new MetaBeanProperty(propName,
    864                            method.getReturnType(),
    865                            findMethod(method), null);
    866
    867                        // add it to the map
    868                        propertyMap.put(propName, mbp);
    869                    }
    870                }
    871                else if(methodName.startsWith("set") &&
    872                    methodName.length() > 3 &&
    873                    method.getParameterTypes().length == 1) {
    874
    875                    // get the name of the property
    876                    String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
    877
    878                    // did we already find the getter of this?
    879                    mp = (MetaProperty) propertyMap.get(propName);
    880                    if(mp != null) {
    881                        if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
    882                            // update the setter method to this one
    883                            ((MetaBeanProperty) mp).setSetter(findMethod(method));
    884                        }
    885                    }
    886                    else {
    887                        // this is a new property to add
    888                        MetaBeanProperty mbp = new MetaBeanProperty(propName,
    889                                                                    method.getParameterTypes()[0],
    890                                                                    null,
    891                                                                    findMethod(method));
    892
    893                        // add it to the map
    894                        propertyMap.put(propName, mbp);
    895                    }
    896                }
    897            }
    898
    899            // now get the super class
    900            klass = klass.getSuperclass();
    901        }
    902    }
    903
    904    /***
    905     * Sets the property value on an object
    906     */
    907    public void setProperty(Object object, String property, Object newValue) {
    908        //
    909        // Unwrap wrapped values fo now - the new MOP will handle them properly
    910        //
    911        if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap();
    912
    913        MetaProperty mp = (MetaProperty) propertyMap.get(property);
    914        if(mp != null) {
    915            try {
    916                mp.setProperty(object, newValue);
    917                return;
    918            }
    919            catch(ReadOnlyPropertyException e) {
    920                // just rethrow it; there's nothing left to do here
    921                throw e;
    922            }
    923            catch (TypeMismatchException e) {
    924                // tried to access to mismatched object.
    925                throw e;
    926            }
    927            catch (Exception e) {
    928                // if the value is a List see if we can construct the value
    929                // from a constructor
    930                if (newValue == null)
    931                    return;
    932                if (newValue instanceof List) {
    933                    List list = (List) newValue;
    934                    int params = list.size();
    935                    Constructor[] constructors = mp.getType().getConstructors();
    936                    for (int i = 0; i < constructors.length; i++) {
    937                        Constructor constructor = constructors[i];
    938                        if (constructor.getParameterTypes().length == params) {
    939                            Object value = MetaClassHelper.doConstructorInvoke(constructor, list.toArray());
    940                            mp.setProperty(object, value);
    941                            return;
    942                        }
    943                    }
    944
    945                    // if value is an array
    946                    Class parameterType = mp.getType();
    947                    if (parameterType.isArray()) {
    948                        Object objArray = MetaClassHelper.asPrimitiveArray(list, parameterType);
    949                        mp.setProperty(object, objArray);
    950                        return;
    951                    }
    952                }
    953
    954                // if value is an multidimensional array
    955                // jes currently this logic only supports metabeansproperties and
    956                // not metafieldproperties. It shouldn't be too hard to support
    957                // the latter...
    958                if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
    959                    MetaBeanProperty mbp = (MetaBeanProperty) mp;
    960                    List list = Arrays.asList((Object[])newValue);
    961                    MetaMethod setter = mbp.getSetter();
    962
    963                    Class parameterType = setter.getParameterTypes()[0];
    964                    Class arrayType = parameterType.getComponentType();
    965                    Object objArray = Array.newInstance(arrayType, list.size());
    966
    967                    for (int i = 0; i < list.size(); i++) {
    968                        List list2 =Arrays.asList((Object[]) list.get(i));
    969                        Object objArray2 = MetaClassHelper.asPrimitiveArray(list2, arrayType);
    970                        Array.set(objArray, i, objArray2);
    971                    }
    972
    973                    MetaClassHelper.doMethodInvoke(object, setter, new Object[]{
    974                        objArray
    975                    });
    976                    return;
    977                }
    978
    979                throw new MissingPropertyException(property, theClass, e);
    980            }
    981        }
    982
    983        RuntimeException runtimeException = null;
    984        MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
    985
    986        try {
    987            if (addListenerMethod != null && newValue instanceof Closure) {
    988                // lets create a dynamic proxy
    989                Object proxy =
    990                    MetaClassHelper.createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
    991                MetaClassHelper.doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
    992                return;
    993            }
    994
    995            if (genericSetMethod == null) {
    996                // Make sure there isn't a generic method in the "use" cases
    997                List possibleGenericMethods = getMethods("set");
    998                if (possibleGenericMethods != null) {
    999                    for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
    1000                        MetaMethod mmethod = (MetaMethod) i.next();
    1001                        Class[] paramTypes = mmethod.getParameterTypes();
    1002                        if (paramTypes.length == 2 && paramTypes[0] == String.class) {
    1003                            Object[] arguments = {property, newValue};
    1004                            Object answer = MetaClassHelper.doMethodInvoke(object, mmethod, arguments);
    1005                            return;
    1006                        }
    1007                    }
    1008                }
    1009            }
    1010            else {
    1011                Object[] arguments = { property, newValue };
    1012                MetaClassHelper.doMethodInvoke(object, genericSetMethod, arguments);
    1013                return;
    1014            }
    1015
    1016            /*** todo or are we an extensible class? */
    1017
    1018            // lets try invoke the set method
    1019            // this is kind of ugly: if it is a protected field, we fall
    1020            // all the way down to this klunky code. Need a better
    1021            // way to handle this situation...
    1022
    1023            String method = "set" + MetaClassHelper.capitalize(property);
    1024            try {
    1025                invokeMethod(object, method, new Object[] { newValue });
    1026            }
    1027            catch (MissingMethodException e1) {
    1028                setAttribute(object,property,newValue);
    1029            }
    1030
    1031        }
    1032        catch (GroovyRuntimeException e) {
    1033            runtimeException = e;
    1034        }
    1035
    1036        if (addListenerMethod==AMBIGOUS_LISTENER_METHOD){
    1037            throw new GroovyRuntimeException("There are multiple listeners for the property "+property+". Please do not use the bean short form to access this listener.");
    1038        } else if (runtimeException!=null) {
    1039            throw new MissingPropertyException(property, theClass, runtimeException);
    1040        }
    1041
    1042
    1043    }
    1044
    1045
    1046    /***
    1047     * Looks up the given attribute (field) on the given object
    1048     */
    1049    public Object getAttribute(final Object object, final String attribute) {
    1050        PrivilegedActionException firstException = null;
    1051
    1052        final Class clazz;
    1053        if (object instanceof Class) {
    1054            clazz=(Class) object;
    1055        } else {
    1056            clazz=theClass;
    1057        }
    1058
    1059        try {
    1060            return AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1061                public Object run() throws NoSuchFieldException, IllegalAccessException {
    1062                    final Field field = clazz.getDeclaredField(attribute);
    1063
    1064                    field.setAccessible(true);
    1065                    return field.get(object);
    1066                }
    1067            });
    1068        } catch (final PrivilegedActionException pae) {
    1069            firstException = pae;
    1070        }
    1071
    1072        try {
    1073            return AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1074                public Object run() throws NoSuchFieldException, IllegalAccessException {
    1075                    final Field field = clazz.getField(attribute);
    1076
    1077                    field.setAccessible(true);
    1078                    return field.get(object);
    1079                }
    1080            });
    1081        } catch (final PrivilegedActionException pae) {
    1082            // prefere the first exception.
    1083        }
    1084
    1085
    1086        if (firstException.getException() instanceof NoSuchFieldException) {
    1087            throw new MissingFieldException(attribute, theClass);
    1088        } else {
    1089            throw new RuntimeException(firstException.getException());
    1090        }
    1091    }
    1092
    1093    /***
    1094     * Sets the given attribute (field) on the given object
    1095     */
    1096    public void setAttribute(final Object object, final String attribute, final Object newValue) {
    1097        PrivilegedActionException firstException = null;
    1098
    1099        final Class clazz;
    1100        if (object instanceof Class) {
    1101            clazz=(Class) object;
    1102        } else {
    1103            clazz=theClass;
    1104        }
    1105
    1106        try {
    1107            AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1108                public Object run() throws NoSuchFieldException, IllegalAccessException {
    1109                    final Field field = clazz.getDeclaredField(attribute);
    1110
    1111                    field.setAccessible(true);
    1112                    field.set(object,newValue);
    1113                    return null;
    1114                }
    1115            });
    1116            return;
    1117        } catch (final PrivilegedActionException pae) {
    1118            firstException = pae;
    1119        }
    1120
    1121        try {
    1122            AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1123                public Object run() throws NoSuchFieldException, IllegalAccessException {
    1124                    final Field field = clazz.getField(attribute);
    1125
    1126                    field.setAccessible(true);
    1127                    field.set(object, newValue);
    1128                    return null;
    1129                }
    1130            });
    1131            return;
    1132        } catch (final PrivilegedActionException pae) {
    1133            // prefere the first exception.
    1134        }
    1135
    1136        if (firstException.getException() instanceof NoSuchFieldException) {
    1137            throw new MissingFieldException(attribute, theClass);
    1138        } else {
    1139            throw new RuntimeException(firstException.getException());
    1140        }
    1141    }
    1142
    1143    public ClassNode getClassNode() {
    1144        if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
    1145            // lets try load it from the classpath
    1146            String className = theClass.getName();
    1147            String groovyFile = className;
    1148            int idx = groovyFile.indexOf('$');
    1149            if (idx > 0) {
    1150                groovyFile = groovyFile.substring(0, idx);
    1151            }
    1152            groovyFile = groovyFile.replace('.', '/') + ".groovy";
    1153
    1154            //System.out.println("Attempting to load: " + groovyFile);
    1155            URL url = theClass.getClassLoader().getResource(groovyFile);
    1156            if (url == null) {
    1157                url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
    1158            }
    1159            if (url != null) {
    1160                try {
    1161
    1162                    /***
    1163                     * todo there is no CompileUnit in scope so class name
    1164                     * checking won't work but that mostly affects the bytecode
    1165                     * generation rather than viewing the AST
    1166                     */
    1167                    CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
    1168                        public void call( ClassVisitor writer, ClassNode node ) {
    1169                            if( node.getName().equals(theClass.getName()) ) {
    1170                                MetaClassImpl.this.classNode = node;
    1171                            }
    1172                        }
    1173                    };
    1174
    1175                    final ClassLoader parent = theClass.getClassLoader();
    1176                    GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
    1177                        public Object run() {
    1178                            return new GroovyClassLoader(parent);
    1179                        }
    1180                    });
    1181                    CompilationUnit unit = new CompilationUnit( );
    1182                    unit.setClassgenCallback( search );
    1183                    unit.addSource( url );
    1184                    unit.compile( Phases.CLASS_GENERATION );
    1185                }
    1186                catch (Exception e) {
    1187                    throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
    1188                }
    1189            }
    1190
    1191        }
    1192        return classNode;
    1193    }
    1194
    1195    public String toString() {
    1196        return super.toString() + "[" + theClass + "]";
    1197    }
    1198
    1199    // Implementation methods
    1200    //-------------------------------------------------------------------------
    1201
    1202    /***
    1203     * Adds all the methods declared in the given class to the metaclass
    1204     * ignoring any matching methods already defined by a derived class
    1205     *
    1206     * @param theClass
    1207     */
    1208    private void addMethods(final Class theClass, boolean forceOverwrite) {
    1209        // add methods directly declared in the class
    1210        Method[] methodArray = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
    1211                public Object run() {
    1212                    return theClass.getDeclaredMethods();
    1213                }
    1214            });
    1215        for (int i = 0; i < methodArray.length; i++) {
    1216            Method reflectionMethod = methodArray[i];
    1217            if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
    1218                // Skip Synthetic methods inserted by JDK 1.5 compilers and later
    1219                continue;
    1220            }
    1221            MetaMethod method = createMetaMethod(reflectionMethod);
    1222            addMethod(method,forceOverwrite);
    1223        }
    1224    }
    1225
    1226    private void addMethod(MetaMethod method, boolean forceOverwrite) {
    1227        String name = method.getName();
    1228
    1229        //System.out.println(theClass.getName() + " == " + name + Arrays.asList(method.getParameterTypes()));
    1230
    1231        if (isGenericGetMethod(method) && genericGetMethod == null) {
    1232            genericGetMethod = method;
    1233        }
    1234        else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
    1235            genericSetMethod = method;
    1236        }
    1237        if (method.isStatic()) {
    1238            List list = (List) staticMethodIndex.get(name);
    1239            if (list == null) {
    1240                list = new ArrayList();
    1241                staticMethodIndex.put(name, list);
    1242                list.add(method);
    1243            }
    1244            else {
    1245                if (!MetaClassHelper.containsMatchingMethod(list, method)) {
    1246                    list.add(method);
    1247                }
    1248            }
    1249        }
    1250
    1251        List list = (List) methodIndex.get(name);
    1252        if (list == null) {
    1253            list = new ArrayList();
    1254            methodIndex.put(name, list);
    1255            list.add(method);
    1256        }
    1257        else {
    1258            if (forceOverwrite) {
    1259                removeMatchingMethod(list,method);
    1260                list.add(method);
    1261            } else if (!MetaClassHelper.containsMatchingMethod(list, method)) {
    1262                list.add(method);
    1263            }
    1264        }
    1265    }
    1266
    1267    /***
    1268     * remove a method of the same matching prototype was found in the list
    1269     */
    1270    private void removeMatchingMethod(List list, MetaMethod method) {
    1271        for (Iterator iter = list.iterator(); iter.hasNext();) {
    1272            MetaMethod aMethod = (MetaMethod) iter.next();
    1273            Class[] params1 = aMethod.getParameterTypes();
    1274            Class[] params2 = method.getParameterTypes();
    1275            if (params1.length == params2.length) {
    1276                boolean matches = true;
    1277                for (int i = 0; i < params1.length; i++) {
    1278                    if (params1[i] != params2[i]) {
    1279                        matches = false;
    1280                        break;
    1281                    }
    1282                }
    1283                if (matches) {
    1284                    iter.remove();
    1285                    return;
    1286                }
    1287            }
    1288        }
    1289        return;
    1290    }
    1291
    1292
    1293    /***
    1294     * Adds all of the newly defined methods from the given class to this
    1295     * metaclass
    1296     *
    1297     * @param theClass
    1298     */
    1299    private void addNewStaticMethodsFrom(Class theClass) {
    1300        MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
    1301        Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
    1302        while (iter.hasNext()) {
    1303            MetaMethod method = (MetaMethod) iter.next();
    1304            if (! newGroovyMethodsList.contains(method)){
    1305                newGroovyMethodsList.add(method);
    1306                addMethod(method,false);
    1307            }
    1308        }
    1309    }
    1310
    1311    /***
    1312     * @return the value of the static property of the given class
    1313     */
    1314    private Object getStaticProperty(Class aClass, String property) {
    1315        //System.out.println("Invoking property: " + property + " on class: "
    1316        // + aClass);
    1317
    1318        // lets try invoke a static getter method
    1319        MetaMethod method = findStaticGetter(aClass, "get" + MetaClassHelper.capitalize(property));
    1320        if (method != null) {
    1321            return MetaClassHelper.doMethodInvoke(aClass, method, MetaClassHelper.EMPTY_ARRAY);
    1322        }
    1323
    1324        //no static getter found, try attribute
    1325        try {
    1326            return getAttribute(aClass,property);
    1327        } catch (MissingFieldException mfe) {
    1328            throw new MissingPropertyException(property, aClass, mfe);
    1329        }
    1330    }
    1331
    1332    /***
    1333     * @return the matching method which should be found
    1334     */
    1335    private 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    private 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    private 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, MetaClassHelper.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    private static Object doConstructorInvokeAt(final Class at, Constructor constructor, Object[] argumentArray) {
    1389        if (log.isLoggable(Level.FINER)) {
    1390            MetaClassHelper.logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
    1391        }
    1392
    1393        // To fix JIRA 435
    1394        // Every constructor should be opened to the accessible classes.
    1395        final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
    1396
    1397        final Constructor ctor = constructor;
    1398        AccessController.doPrivileged(new PrivilegedAction() {
    1399            public Object run() {
    1400                ctor.setAccessible(accessible);
    1401                return null;
    1402            }
    1403        });
    1404        return MetaClassHelper.doConstructorInvoke(constructor,argumentArray);
    1405    }
    1406
    1407    /***
    1408     * Chooses the correct method to use from a list of methods which match by
    1409     * name.
    1410     *
    1411     * @param methods
    1412     *            the possible methods to choose from
    1413     * @param arguments
    1414     *            the original argument to the method
    1415     * @return
    1416     */
    1417    private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
    1418        int methodCount = methods.size();
    1419        if (methodCount <= 0) {
    1420            return null;
    1421        }
    1422        else if (methodCount == 1) {
    1423            Object method = methods.get(0);
    1424            if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
    1425                return method;
    1426            }
    1427            return null;
    1428        }
    1429        Object answer = null;
    1430        if (arguments == null || arguments.length == 0) {
    1431            answer = MetaClassHelper.chooseEmptyMethodParams(methods);
    1432        }
    1433        else if (arguments.length == 1 && arguments[0] == null) {
    1434            answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
    1435        }
    1436        else {
    1437            List matchingMethods = new ArrayList();
    1438
    1439            for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1440                Object method = iter.next();
    1441
    1442                // making this false helps find matches
    1443                if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
    1444                    matchingMethods.add(method);
    1445                }
    1446            }
    1447            if (matchingMethods.isEmpty()) {
    1448                return null;
    1449            }
    1450            else if (matchingMethods.size() == 1) {
    1451                return matchingMethods.get(0);
    1452            }
    1453            return chooseMostSpecificParams(methodName, matchingMethods, arguments);
    1454
    1455        }
    1456        if (answer != null) {
    1457            return answer;
    1458        }
    1459        throw new GroovyRuntimeException(
    1460            "Could not find which method to invoke from this list: "
    1461                + methods
    1462                + " for arguments: "
    1463                + InvokerHelper.toString(arguments));
    1464    }
    1465
    1466    private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
    1467
    1468        Class[] wrappedArguments = MetaClassHelper.wrap(arguments);
    1469
    1470        int matchesDistance = -1;
    1471        LinkedList matches = new LinkedList();
    1472        for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
    1473            Object method = iter.next();
    1474            Class[] paramTypes = MetaClassHelper.getParameterTypes(method);
    1475            if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
    1476            int dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
    1477            if (matches.size()==0) {
    1478                matches.add(method);
    1479                matchesDistance = dist;
    1480            } else if (dist<matchesDistance) {
    1481                matchesDistance=dist;
    1482                matches.clear();
    1483                matches.add(method);
    1484            } else if (dist==matchesDistance) {
    1485                matches.add(method);
    1486            }
    1487
    1488        }
    1489        if (matches.size()==1) {
    1490            return matches.getFirst();
    1491        }
    1492        if (matches.size()==0) {
    1493            return null;
    1494        }
    1495
    1496        //more than one matching method found --> ambigous!
    1497        String msg = "Ambiguous method overloading for method ";
    1498        msg+= theClass.getName()+"#"+name;
    1499        msg+= ".\nCannot resolve which method to invoke for ";
    1500        msg+= InvokerHelper.toString(arguments);
    1501        msg+= " due to overlapping prototypes between:";
    1502        for (Iterator iter = matches.iterator(); iter.hasNext();) {
    1503            Class[] types=MetaClassHelper.getParameterTypes(iter.next());
    1504            msg+= "\n\t"+InvokerHelper.toString(types);
    1505        }
    1506        throw new GroovyRuntimeException(msg);
    1507    }
    1508
    1509    private boolean isGenericGetMethod(MetaMethod method) {
    1510        if (method.getName().equals("get")) {
    1511            Class[] parameterTypes = method.getParameterTypes();
    1512            return parameterTypes.length == 1 && parameterTypes[0] == String.class;
    1513        }
    1514        return false;
    1515    }
    1516
    1517    /***
    1518     * Call this method when any mutation method is called, such as adding a new
    1519     * method to this MetaClass so that any caching or bytecode generation can be
    1520     * regenerated.
    1521     */
    1522    private synchronized void onMethodChange() {
    1523        reflector = null;
    1524    }
    1525
    1526    public synchronized void checkInitialised() {
    1527        if (!initialised) {
    1528            initialised = true;
    1529            addInheritedMethods();
    1530        }
    1531        if (reflector == null) {
    1532            generateReflector();
    1533        }
    1534    }
    1535
    1536    private MetaMethod createMetaMethod(final Method method) {
    1537        if (registry.useAccessible()) {
    1538            AccessController.doPrivileged(new PrivilegedAction() {
    1539                public Object run() {
    1540                    method.setAccessible(true);
    1541                    return null;
    1542                }
    1543            });
    1544        }
    1545
    1546        MetaMethod answer = new MetaMethod(method);
    1547        if (isValidReflectorMethod(answer)) {
    1548            allMethods.add(answer);
    1549            answer.setMethodIndex(allMethods.size());
    1550        }
    1551        else {
    1552            //log.warning("Creating reflection based dispatcher for: " + method);
    1553            answer = new ReflectionMetaMethod(method);
    1554        }
    1555
    1556        if (useReflection) {
    1557            //log.warning("Creating reflection based dispatcher for: " + method);
    1558            return new ReflectionMetaMethod(method);
    1559        }
    1560
    1561        return answer;
    1562    }
    1563
    1564    private boolean isValidReflectorMethod(MetaMethod method) {
    1565        // We cannot use a reflector if the method is private, protected, or package accessible only.
    1566        if (!method.isPublic()) {
    1567            return false;
    1568        }
    1569        // lets see if this method is implemented on an interface
    1570        List interfaceMethods = getInterfaceMethods();
    1571        for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) {
    1572            MetaMethod aMethod = (MetaMethod) iter.next();
    1573            if (method.isSame(aMethod)) {
    1574                method.setInterfaceClass(aMethod.getDeclaringClass());
    1575                return true;
    1576            }
    1577        }
    1578        // it's no interface method, so try to find the highest class
    1579        // in hierarchy defining this method
    1580        Class declaringClass = method.getDeclaringClass();
    1581        for (Class clazz=declaringClass; clazz!=null; clazz=clazz.getSuperclass()) {
    1582            try {
    1583                final Class klazz = clazz;
    1584                final String mName = method.getName();
    1585                final Class[] parms = method.getParameterTypes();
    1586                try {
    1587                    Method m = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1588                        public Object run() throws NoSuchMethodException {
    1589                            return klazz.getDeclaredMethod(mName, parms);
    1590                        }
    1591                    });
    1592                    if (!Modifier.isPublic(clazz.getModifiers())) continue;
    1593                    if (!Modifier.isPublic(m.getModifiers())) continue;
    1594                    declaringClass = clazz;
    1595                } catch (PrivilegedActionException pae) {
    1596                    if (pae.getException() instanceof NoSuchMethodException) {
    1597                        throw (NoSuchMethodException) pae.getException();
    1598                    } else {
    1599                        throw new RuntimeException(pae.getException());
    1600                    }
    1601                }
    1602            } catch (SecurityException e) {
    1603                continue;
    1604            } catch (NoSuchMethodException e) {
    1605                continue;
    1606            }
    1607        }
    1608        if (!Modifier.isPublic(declaringClass.getModifiers())) return false;
    1609        method.setDeclaringClass(declaringClass);
    1610
    1611        return true;
    1612    }
    1613
    1614    private void generateReflector() {
    1615        reflector = loadReflector(allMethods);
    1616        if (reflector == null) {
    1617            throw new RuntimeException("Should have a reflector for "+theClass.getName());
    1618        }
    1619        // lets set the reflector on all the methods
    1620        for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
    1621            MetaMethod metaMethod = (MetaMethod) iter.next();
    1622            metaMethod.setReflector(reflector);
    1623        }
    1624    }
    1625
    1626    private String getReflectorName() {
    1627        String className = theClass.getName();
    1628        String packagePrefix = "gjdk.";
    1629        String name = packagePrefix + className + "_GroovyReflector";
    1630        if (theClass.isArray()) {
    1631        	   Class clazz = theClass;
    1632        	   name = packagePrefix;
    1633        	   int level = 0;
    1634        	   while (clazz.isArray()) {
    1635        	   	  clazz = clazz.getComponentType();
    1636        	   	  level++;
    1637        	   }
    1638            String componentName = clazz.getName();
    1639            name = packagePrefix + componentName + "_GroovyReflectorArray";
    1640            if (level>1) name += level;
    1641        }
    1642        return name;
    1643    }
    1644
    1645    private Reflector loadReflector(List methods) {
    1646        String name = getReflectorName();
    1647        /*
    1648         * Lets generate it && load it.
    1649         */
    1650        ReflectorGenerator generator = new ReflectorGenerator(methods);
    1651        try {
    1652            ClassWriter cw = new ClassWriter(true);
    1653            generator.generate(cw, name);
    1654            byte[] bytecode = cw.toByteArray();
    1655
    1656            /*try {
    1657             FileOutputStream fis = new FileOutputStream(name);
    1658             fis.write(bytecode);
    1659             fis.close();
    1660             } catch (IOException ioe){}*/
    1661            ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
    1662                public Object run() {
    1663                    return theClass.getClassLoader();
    1664                }
    1665            });
    1666            Class type = registry.createReflectorClass(loader, name, bytecode);
    1667            return (Reflector) type.newInstance();
    1668        }
    1669        catch (Exception e) {
    1670            e.printStackTrace();
    1671            throw new GroovyRuntimeException("Could not generate and load the reflector for class: " + name + ". Reason: " + e, e);
    1672        }
    1673    }
    1674
    1675    public List getMethods() {
    1676        return allMethods;
    1677    }
    1678
    1679    public List getMetaMethods() {
    1680        return new ArrayList(newGroovyMethodsList);
    1681    }
    1682
    1683    private synchronized List getInterfaceMethods() {
    1684        if (interfaceMethods == null) {
    1685            interfaceMethods = new ArrayList();
    1686            Class type = theClass;
    1687            while (type != null) {
    1688                Class[] interfaces = type.getInterfaces();
    1689                for (int i = 0; i < interfaces.length; i++) {
    1690                    Class iface = interfaces[i];
    1691                    Method[] methods = iface.getMethods();
    1692                    addInterfaceMethods(interfaceMethods, methods);
    1693                }
    1694                type = type.getSuperclass();
    1695            }
    1696        }
    1697        return interfaceMethods;
    1698    }
    1699
    1700    private void addInterfaceMethods(List list, Method[] methods) {
    1701        for (int i = 0; i < methods.length; i++) {
    1702            list.add(createMetaMethod(methods[i]));
    1703        }
    1704    }
    1705 }