View Javadoc

    1   /*
    2   $Id: MetaClassImpl.java 4669 2007-01-02 19:35:47Z blackdrag $
    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.Constructor;
    54  import java.lang.reflect.Field;
    55  import java.lang.reflect.Method;
    56  import java.lang.reflect.Modifier;
    57  import java.net.URL;
    58  import java.security.AccessController;
    59  import java.security.PrivilegedAction;
    60  import java.security.PrivilegedActionException;
    61  import java.security.PrivilegedExceptionAction;
    62  import java.util.ArrayList;
    63  import java.util.Arrays;
    64  import java.util.Collection;
    65  import java.util.Collections;
    66  import java.util.Comparator;
    67  import java.util.HashMap;
    68  import java.util.HashSet;
    69  import java.util.Iterator;
    70  import java.util.LinkedList;
    71  import java.util.List;
    72  import java.util.Map;
    73  import java.util.Set;
    74  import java.util.logging.Level;
    75
    76  import org.codehaus.groovy.GroovyBugError;
    77  import org.codehaus.groovy.ast.ClassNode;
    78  import org.codehaus.groovy.classgen.BytecodeHelper;
    79  import org.codehaus.groovy.control.CompilationUnit;
    80  import org.codehaus.groovy.control.Phases;
    81  import org.codehaus.groovy.runtime.CurriedClosure;
    82  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    83  import org.codehaus.groovy.runtime.DefaultMethodKey;
    84  import org.codehaus.groovy.runtime.GroovyCategorySupport;
    85  import org.codehaus.groovy.runtime.InvokerHelper;
    86  import org.codehaus.groovy.runtime.MetaClassHelper;
    87  import org.codehaus.groovy.runtime.MethodClosure;
    88  import org.codehaus.groovy.runtime.MethodKey;
    89  import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
    90  import org.codehaus.groovy.runtime.NewStaticMetaMethod;
    91  import org.codehaus.groovy.runtime.ReflectionMetaMethod;
    92  import org.codehaus.groovy.runtime.Reflector;
    93  import org.codehaus.groovy.runtime.TransformMetaMethod;
    94  import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
    95  import org.codehaus.groovy.runtime.wrappers.Wrapper;
    96  import org.objectweb.asm.ClassVisitor;
    97
    98  /***
    99  * Allows methods to be dynamically added to existing classes at runtime
    100 *
    101 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    102 * @author Guillaume Laforge
    103 * @author Jochen Theodorou
    104 * @version $Revision: 4669 $
    105 * @see groovy.lang.MetaClass
    106 */
    107 public class MetaClassImpl extends MetaClass {
    108
    109    protected MetaClassRegistry registry;
    110    private ClassNode classNode;
    111    private Map classMethodIndex = new HashMap();
    112    private Map classMethodIndexForSuper;
    113    private Map classStaticMethodIndex = new HashMap();
    114    private Map classPropertyIndex = new HashMap();
    115    private Map classPropertyIndexForSuper = new HashMap();
    116    private Map staticPropertyIndex = new HashMap();
    117    private Map listeners = new HashMap();
    118    private Map methodCache = Collections.synchronizedMap(new HashMap());
    119    private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
    120    private MetaMethod genericGetMethod;
    121    private MetaMethod genericSetMethod;
    122    private List constructors;
    123    private List allMethods = new ArrayList();
    124    private List interfaceMethods;
    125    private Reflector reflector;
    126    private boolean initialized;
    127    // we only need one of these that can be reused over and over.
    128    private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
    129    private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new Class[]{},null,0);
    130    private static final Object[] EMPTY_ARGUMENTS = {};
    131    private List newGroovyMethodsList = new LinkedList();
    132
    133    public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
    134        super(theClass);
    135        this.registry = registry;
    136
    137        constructors = (List) AccessController.doPrivileged(new  PrivilegedAction() {
    138                public Object run() {
    139                    return Arrays.asList (theClass.getDeclaredConstructors());
    140                }
    141            });
    142    }
    143
    144    private void fillMethodIndex() {
    145        LinkedList superClasses = getSuperClasses();
    146        // let's add all the base class methods
    147        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    148            Class c = (Class) iter.next();
    149            addMethods(c);
    150        }
    151
    152        Set interfaces = new HashSet();
    153        makeInterfaceSet(theClass,interfaces);
    154
    155        inheritMethods(superClasses,classMethodIndex);
    156        inheritInterfaceMethods(interfaces);
    157        copyClassMethodIndexForSuper();
    158
    159        connectMultimethods(superClasses);
    160        populateInterfaces(interfaces);
    161        removeMultimethodsOverloadedWithPrivateMethods();
    162
    163        replaceWithMOPCalls();
    164    }
    165
    166    private LinkedList getSuperClasses() {
    167        LinkedList superClasses = new LinkedList();
    168        for (Class c = theClass; c!= null; c = c.getSuperclass()) {
    169            superClasses.addFirst(c);
    170        }
    171        if (theClass.isArray() && theClass!=Object[].class && !theClass.getComponentType().isPrimitive()) {
    172            superClasses.addFirst(Object[].class);
    173        }
    174        return superClasses;
    175    }
    176
    177    private void removeMultimethodsOverloadedWithPrivateMethods() {
    178        Map privates = new HashMap();
    179        MethodIndexAction mia = new MethodIndexAction() {
    180            public List methodNameAction(Class clazz, String methodName, List methods) {
    181               boolean hasPrivate=false;
    182               for (Iterator iter = methods.iterator(); iter.hasNext();) {
    183                   MetaMethod method = (MetaMethod) iter.next();
    184                   if (method.isPrivate() && clazz == method.getDeclaringClass()) {
    185                       hasPrivate = true;
    186                       break;
    187                   }
    188               }
    189               if (!hasPrivate) return null;
    190               // We have private methods for that name, so remove the
    191               // multimethods. That is the same as in our index for
    192               // super, so just copy the list from there. It is not
    193               // possible to use a pointer here, because the methods
    194               // in the index for super are replaced later by MOP
    195               // methods like super$5$foo
    196               methods.clear();
    197               methods.addAll((Collection) ((Map) classMethodIndexForSuper.get(clazz)).get(methodName));
    198               return methods;
    199            }
    200            public boolean replaceMethodList() {return false;}
    201        };
    202        mia.iterate(classMethodIndex);
    203    }
    204
    205
    206    private void replaceWithMOPCalls() {
    207        // no MOP methods if not a child of GroovyObject
    208        if (!GroovyObject.class.isAssignableFrom(theClass)) return;
    209
    210        final Map mainClassMethodIndex = (Map) classMethodIndex.get(theClass);
    211        class MOPIter extends MethodIndexAction {
    212            boolean useThis;
    213            public boolean skipClass(Class clazz) {
    214                return !useThis && clazz==theClass;
    215            }
    216            public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {
    217                String mopName = getMOPMethodName(method.getDeclaringClass(), methodName,useThis);
    218                List matches = (List) mainClassMethodIndex.get(mopName);
    219                if (matches==null) {
    220                    newList.add(method);
    221                    return;
    222                }
    223                matches = new ArrayList(matches);
    224                MetaMethod matchingMethod = removeMatchingMethod(matches,method);
    225                if (matchingMethod==null) {
    226                    newList.add(method);
    227                    return;
    228                } else {
    229                    newList.add(matchingMethod);
    230                }
    231            }
    232        }
    233        MOPIter iter = new MOPIter();
    234
    235        // replace all calls for super with the correct MOP method
    236        iter.useThis = false;
    237        iter.iterate(classMethodIndexForSuper);
    238        // replace all calls for this with the correct MOP method
    239        iter.useThis = true;
    240        iter.iterate(classMethodIndex);
    241    }
    242
    243    private String getMOPMethodName(Class declaringClass, String name, boolean useThis) {
    244        int distance = 0;
    245        for (;declaringClass!=null; declaringClass=declaringClass.getSuperclass()) {
    246            distance++;
    247        }
    248        return (useThis?"this":"super")+"$"+distance+"$"+name;
    249    }
    250
    251    private void copyClassMethodIndexForSuper() {
    252        classMethodIndexForSuper = new HashMap(classMethodIndex.size());
    253        for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) {
    254            Map.Entry cmiEntry = (Map.Entry) iter.next();
    255            Map methodIndex = (Map) cmiEntry.getValue();
    256            Map copy = new HashMap (methodIndex.size());
    257            for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
    258                Map.Entry mEntry = (Map.Entry) iterator.next();
    259                copy.put(mEntry.getKey(), new ArrayList((List) mEntry.getValue()));
    260            }
    261            classMethodIndexForSuper.put(cmiEntry.getKey(),copy);
    262        }
    263    }
    264
    265    private void inheritInterfaceMethods(Set interfaces) {
    266        // add methods declared by DGM for interfaces
    267        List methods = registry.getInstanceMethods();
    268        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    269            Method element = (Method) iter.next();
    270            Class dgmClass = element.getParameterTypes()[0];
    271            if (!interfaces.contains(dgmClass)) continue;
    272            NewInstanceMetaMethod method = new NewInstanceMetaMethod(createMetaMethod(element));
    273            if (! newGroovyMethodsList.contains(method)){
    274                newGroovyMethodsList.add(method);
    275            }
    276            Map methodIndex = (Map) classMethodIndex.get(theClass);
    277            List list = (List) methodIndex.get(method.getName());
    278            if (list == null) {
    279                list = new ArrayList();
    280                methodIndex.put(method.getName(), list);
    281                list.add(method);
    282            } else {
    283                addMethodToList(list,method);
    284            }
    285        }
    286        methods = registry.getStaticMethods();
    287        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    288            Method element = (Method) iter.next();
    289            Class dgmClass = element.getParameterTypes()[0];
    290            if (!interfaces.contains(dgmClass)) continue;
    291            addNewStaticMethod(element);
    292        }
    293    }
    294
    295    private void populateInterfaces(Set interfaces){
    296        Map currentIndex = (Map) classMethodIndex.get(theClass);
    297        Map index = new HashMap();
    298        copyNonPrivateMethods(currentIndex,index);
    299        for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
    300            Class iClass = (Class) iter.next();
    301            Map methodIndex = (Map) classMethodIndex.get(iClass);
    302            if (methodIndex==null || methodIndex.size()==0) {
    303                classMethodIndex.put(iClass,index);
    304                continue;
    305            }
    306            copyNonPrivateMethods(currentIndex,methodIndex);
    307        }
    308    }
    309
    310    private static void makeInterfaceSet(Class c, Set s) {
    311        if (c==null) return;
    312        Class[] interfaces = c.getInterfaces();
    313        for (int i = 0; i < interfaces.length; i++) {
    314            if (!s.contains(interfaces[i])) {
    315                s.add(interfaces[i]);
    316                makeInterfaceSet(interfaces[i],s);
    317            }
    318        }
    319        makeInterfaceSet(c.getSuperclass(),s);
    320    }
    321
    322    private void copyNonPrivateMethods(Map from, Map to) {
    323        for (Iterator iterator = from.entrySet().iterator(); iterator.hasNext();) {
    324            Map.Entry element = (Map.Entry) iterator.next();
    325            List oldList = (List) element.getValue();
    326            List newList = (List) to.get(element.getKey());
    327            if (newList==null) {
    328                to.put(element.getKey(),new ArrayList(oldList));
    329            } else {
    330                addNonPrivateMethods(newList,oldList);
    331            }
    332        }
    333    }
    334
    335    private void connectMultimethods(List superClasses){
    336        superClasses = DefaultGroovyMethods.reverse(superClasses);
    337        Map last = null;
    338        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    339            Class c = (Class) iter.next();
    340            Map methodIndex = (Map) classMethodIndex.get(c);
    341            if (methodIndex==last) continue;
    342            if (last!=null) copyNonPrivateMethods(last,methodIndex);
    343            last = methodIndex;
    344        }
    345    }
    346
    347    private void inheritMethods(Collection superClasses, Map classMethodIndex){
    348        Map last = null;
    349        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    350            Class c = (Class) iter.next();
    351            Map methodIndex = (Map) classMethodIndex.get(c);
    352            if (last!=null) {
    353                if (methodIndex.size()==0) {
    354                    classMethodIndex.put(c,last);
    355                    continue;
    356                }
    357                copyNonPrivateMethods(last,methodIndex);
    358            }
    359            last = methodIndex;
    360        }
    361    }
    362
    363    private void addNonPrivateMethods(List newList, List oldList) {
    364        for (Iterator iter = oldList.iterator(); iter.hasNext();) {
    365            MetaMethod element = (MetaMethod) iter.next();
    366            if (element.isPrivate()) continue;
    367            addMethodToList(newList,element);
    368        }
    369    }
    370
    371 /***
    372     * @return all the normal instance methods avaiable on this class for the
    373     *         given name
    374     */
    375    private List getMethods(Class sender, String name, boolean isCallToSuper) {
    376        Map methodIndex;
    377        if (isCallToSuper) {
    378            methodIndex = (Map) classMethodIndexForSuper.get(sender);
    379        } else {
    380            methodIndex = (Map) classMethodIndex.get(sender);
    381        }
    382        List answer;
    383        if (methodIndex!=null) {
    384            answer = (List) methodIndex.get(name);
    385            if (answer == null) answer = Collections.EMPTY_LIST;
    386        } else {
    387            answer = Collections.EMPTY_LIST;
    388        }
    389
    390        if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) {
    391            List used = GroovyCategorySupport.getCategoryMethods(sender, name);
    392            if (used != null) {
    393                answer = new ArrayList(answer);
    394                for (Iterator iter = used.iterator(); iter.hasNext();) {
    395                    MetaMethod element = (MetaMethod) iter.next();
    396                    removeMatchingMethod(answer,element);
    397                }
    398                answer.addAll(used);
    399            }
    400        }
    401        return answer;
    402    }
    403
    404    /***
    405     * @return all the normal static methods avaiable on this class for the
    406     *         given name
    407     */
    408    private List getStaticMethods(Class sender, String name) {
    409        Map methodIndex = (Map) classStaticMethodIndex.get(sender);
    410        if (methodIndex == null) return Collections.EMPTY_LIST;
    411        List answer = (List) methodIndex.get(name);
    412        if (answer == null) return Collections.EMPTY_LIST;
    413        return answer;
    414    }
    415
    416    public void addNewInstanceMethod(Method method) {
    417        NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
    418        if (! newGroovyMethodsList.contains(newMethod)){
    419            newGroovyMethodsList.add(newMethod);
    420            addMetaMethod(newMethod);
    421        }
    422    }
    423
    424    public void addNewStaticMethod(Method method) {
    425        NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
    426        if (! newGroovyMethodsList.contains(newMethod)){
    427            newGroovyMethodsList.add(newMethod);
    428            addMetaMethod(newMethod);
    429        }
    430    }
    431
    432    private void unwrap(Object[] arguments) {
    433        //
    434        // Temp code to ignore wrapped parameters
    435        // The New MOP will deal with these properly
    436        //
    437        for (int i = 0; i != arguments.length; i++) {
    438         if (arguments[i] instanceof Wrapper) {
    439           arguments[i] = ((Wrapper)arguments[i]).unwrap();
    440         }
    441        }
    442    }
    443
    444
    445    /***
    446     * Invokes the given method on the object.
    447     * @deprecated
    448     */
    449    public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
    450        return invokeMethod(theClass,object,methodName,originalArguments,false,false);
    451    }
    452
    453
    454    /***
    455     * Invokes the given method on the object.
    456     *
    457     */
    458    public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
    459        checkInitalised();
    460        if (object == null) {
    461            throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
    462        }
    463        if (log.isLoggable(Level.FINER)){
    464            MetaClassHelper.logMethodCall(object, methodName, originalArguments);
    465        }
    466        Object[] arguments = originalArguments;
    467        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    468        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    469        unwrap(arguments);
    470
    471        MetaMethod method = getMethodWithCaching(sender, methodName, argClasses, isCallToSuper);
    472
    473        if (method==null && arguments.length==1 && arguments[0] instanceof List) {
    474            Object[] newArguments = ((List) arguments[0]).toArray();
    475            Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
    476            method = getMethodWithCaching(sender, methodName, newArgClasses, isCallToSuper);
    477            if (method!=null) {
    478                MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses, isCallToSuper);
    479                method = new TransformMetaMethod(method) {
    480                    public Object invoke(Object object, Object[] arguments) {
    481                        Object firstArgument = arguments[0];
    482                        List list = (List) firstArgument;
    483                        arguments = list.toArray();
    484                        return super.invoke(object, arguments);
    485                    }
    486                };
    487                cacheInstanceMethod(methodKey, method);
    488                return invokeMethod(sender,object,methodName, originalArguments, isCallToSuper, fromInsideClass);
    489            }
    490        }
    491
    492        boolean isClosure = object instanceof Closure;
    493        if (isClosure) {
    494            Closure closure = (Closure) object;
    495            Object delegate = closure.getDelegate();
    496            Object owner = closure.getOwner();
    497
    498
    499            if ("call".equals(methodName) || "doCall".equals(methodName)) {
    500                if (object.getClass()==MethodClosure.class) {
    501                    MethodClosure mc = (MethodClosure) object;
    502                    methodName = mc.getMethod();
    503                    Class ownerClass = owner.getClass();
    504                    if (owner instanceof Class) ownerClass = (Class) owner;
    505                    MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
    506                    return ownerMetaClass.invokeMethod(ownerClass,owner,methodName,arguments,false,false);
    507                } else if (object.getClass()==CurriedClosure.class) {
    508                    CurriedClosure cc = (CurriedClosure) object;
    509                    // change the arguments for an uncurried call
    510                    arguments = cc.getUncurriedArguments(arguments);
    511                    Class ownerClass = owner.getClass();
    512                    if (owner instanceof Class) ownerClass = (Class) owner;
    513                    MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
    514                    return ownerMetaClass.invokeMethod(owner,methodName,arguments);
    515                }
    516            } else if ("curry".equals(methodName)) {
    517                return closure.curry(arguments);
    518            }
    519
    520            if (method==null && owner!=closure) {
    521                Class ownerClass = owner.getClass();
    522                if (owner instanceof Class) ownerClass = (Class) owner;
    523                MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
    524                method = ownerMetaClass.pickMethod(methodName,argClasses);
    525                if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
    526            }
    527            if (method==null && delegate!=closure && delegate!=null) {
    528                Class delegateClass = delegate.getClass();
    529                if (delegate instanceof Class) delegateClass = (Class) delegate;
    530                MetaClass delegateMetaClass = registry.getMetaClass(delegateClass);
    531                method = delegateMetaClass.pickMethod(methodName,argClasses);
    532                if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
    533            }
    534            if (method==null) {
    535                // still no methods found, test if delegate or owner are GroovyObjects
    536                // and invoke the method on them if so.
    537                MissingMethodException last = null;
    538                if (owner!=closure && (owner instanceof GroovyObject)) {
    539                    try {
    540                        GroovyObject go = (GroovyObject) owner;
    541                        return go.invokeMethod(methodName,originalArguments);
    542                    } catch (MissingMethodException mme) {
    543                        if (last==null) last = mme;
    544                    }
    545                }
    546                if (delegate!=closure && (delegate instanceof GroovyObject)) {
    547                    try {
    548                        GroovyObject go = (GroovyObject) delegate;
    549                        return go.invokeMethod(methodName,originalArguments);
    550                    } catch (MissingMethodException mme) {
    551                        last = mme;
    552                    }
    553                }
    554                if (last!=null) throw last;
    555            }
    556
    557        }
    558
    559        if (method != null) {
    560            return MetaClassHelper.doMethodInvoke(object, method, arguments);
    561        } else {
    562            // if no method was found, try to find a closure defined as a field of the class and run it
    563            try {
    564                Object value = this.getProperty(object, methodName);
    565                if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
    566                    Closure closure = (Closure) value;
    567                    MetaClass delegateMetaClass = closure.getMetaClass();
    568                    return delegateMetaClass.invokeMethod(closure.getClass(),closure,"doCall",originalArguments,false,fromInsideClass);
    569                }
    570            } catch (MissingPropertyException mpe) {}
    571
    572            throw new MissingMethodException(methodName, theClass, originalArguments, false);
    573        }
    574    }
    575
    576    public MetaMethod getMethodWithCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
    577        // lets try use the cache to find the method
    578        if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) {
    579            return getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
    580        } else {
    581            MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments, isCallToSuper);
    582            MetaMethod method = (MetaMethod) methodCache.get(methodKey);
    583            if (method == null) {
    584                method = getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
    585                cacheInstanceMethod(methodKey, method);
    586            }
    587            return method;
    588        }
    589    }
    590
    591    protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
    592        if (method != null && method.isCacheable()) {
    593            methodCache.put(key, method);
    594        }
    595    }
    596
    597    protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
    598        if (method != null && method.isCacheable()) {
    599            staticMethodCache.put(key, method);
    600        }
    601    }
    602
    603
    604    public Constructor retrieveConstructor(Class[] arguments) {
    605        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
    606        if (constructor != null) {
    607            return constructor;
    608        }
    609        constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
    610        if (constructor != null) {
    611            return constructor;
    612        }
    613        return null;
    614    }
    615
    616    public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
    617        MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments, false);
    618        MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    619        if (method == null) {
    620            method = pickStaticMethod(theClass,methodName, arguments);
    621            cacheStaticMethod(methodKey, method);
    622        }
    623        return method;
    624    }
    625
    626    public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
    627        MetaMethod method = null;
    628        List methods = getMethods(sender,methodName,isCallToSuper);
    629        if (methods!=null && !methods.isEmpty()) {
    630            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    631        }
    632        return method;
    633    }
    634
    635    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
    636        checkInitalised();
    637        if (log.isLoggable(Level.FINER)){
    638            MetaClassHelper.logMethodCall(object, methodName, arguments);
    639        }
    640
    641        Class sender = object.getClass();
    642        if (object instanceof Class) sender = (Class) object;
    643        if (sender!=theClass) {
    644            MetaClass mc = registry.getMetaClass(sender);
    645            return mc.invokeStaticMethod(sender,methodName,arguments);
    646        }
    647        if (sender==Class.class) {
    648            return invokeMethod(object,methodName,arguments);
    649        }
    650
    651        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    652        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    653        unwrap(arguments);
    654
    655        // lets try use the cache to find the method
    656        MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses, false);
    657        MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
    658        if (method == null) {
    659            method = pickStaticMethod(sender, methodName, argClasses);
    660            cacheStaticMethod(methodKey.createCopy(), method);
    661        }
    662
    663        if (method != null) {
    664            return MetaClassHelper.doMethodInvoke(object, method, arguments);
    665        }
    666
    667        throw new MissingMethodException(methodName, sender, arguments, true);
    668    }
    669
    670    private MetaMethod pickStaticMethod(Class sender, String methodName, Class[] arguments) {
    671        MetaMethod method = null;
    672        List methods = getStaticMethods(sender,methodName);
    673
    674        if (!methods.isEmpty()) {
    675            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
    676        }
    677        if (method == null && theClass != Class.class) {
    678            MetaClass classMetaClass = registry.getMetaClass(Class.class);
    679            method = classMetaClass.pickMethod(methodName, arguments);
    680        }
    681        if (method == null) {
    682            method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
    683        }
    684        return method;
    685    }
    686
    687    public Object invokeConstructor(Object[] arguments) {
    688        return invokeConstructor(theClass,arguments,false);
    689    }
    690
    691    public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
    692        //TODO: that is just a quick prototype, not the real thing!
    693        if (numberOfCosntructors != constructors.size()) {
    694            throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for "+
    695                this.theClass.getName()+" do not match. Expected "+numberOfCosntructors+" but got "+constructors.size());
    696        }
    697
    698        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    699        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    700        unwrap(arguments);
    701        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
    702        if (constructor == null) {
    703            constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
    704        }
    705        if (constructor==null) {
    706            throw new GroovyRuntimeException(
    707                    "Could not find matching constructor for: "
    708                        + theClass.getName()
    709                        + "("+InvokerHelper.toTypeString(arguments)+")");
    710        }
    711        List l = new ArrayList(constructors);
    712        Comparator comp = new Comparator() {
    713            public int compare(Object arg0, Object arg1) {
    714                Constructor c0 = (Constructor) arg0;
    715                Constructor c1 = (Constructor) arg1;
    716                String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getParameterTypes());
    717                String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getParameterTypes());
    718                return descriptor0.compareTo(descriptor1);
    719            }
    720        };
    721        Collections.sort(l,comp);
    722        int found = -1;
    723        for (int i=0; i<l.size(); i++) {
    724            if (l.get(i)!=constructor) continue;
    725            found = i;
    726            break;
    727        }
    728        // NOTE: must be changed to "1 |" if constructor was vargs
    729        int ret = 0 | (found << 8);
    730        return ret;
    731    }
    732
    733    /***
    734     * checks if the initialisation of the class id complete.
    735     * This method should be called as a form of assert, it is no
    736     * way to test if there is still initialisation work to be done.
    737     * Such logic must be implemented in a different way.
    738     * @throws IllegalStateException if the initialisation is incomplete yet
    739     */
    740    protected void checkInitalised() {
    741        if (!isInitialized())
    742            throw new IllegalStateException(
    743                    "initialize must be called for meta " +
    744                    "class of "+ theClass +
    745                    "("+this.getClass() + ") " +
    746                    "to complete initialisation process " +
    747                    "before any invocation or field/property " +
    748                    "access can be done");
    749    }
    750
    751    private Object invokeConstructor(Class at, Object[] arguments, boolean setAccessible) {
    752        checkInitalised();
    753        if (arguments==null) arguments = EMPTY_ARGUMENTS;
    754        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
    755        unwrap(arguments);
    756        Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
    757        if (constructor != null) {
    758            return doConstructorInvoke(at, constructor, arguments, true);
    759        }
    760        constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
    761        if (constructor != null) {
    762            return doConstructorInvoke(at, constructor, arguments, true);
    763        }
    764
    765        if (arguments.length == 1) {
    766            Object firstArgument = arguments[0];
    767            if (firstArgument instanceof Map) {
    768                constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
    769                if (constructor != null) {
    770                    Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true);
    771                    setProperties(bean, ((Map) firstArgument));
    772                    return bean;
    773                }
    774            }
    775        }
    776        throw new GroovyRuntimeException(
    777                    "Could not find matching constructor for: "
    778                        + theClass.getName()
    779                        + "("+InvokerHelper.toTypeString(arguments)+")");
    780    }
    781
    782    /***
    783     * Sets a number of bean properties from the given Map where the keys are
    784     * the String names of properties and the values are the values of the
    785     * properties to set
    786     */
    787    public void setProperties(Object bean, Map map) {
    788        checkInitalised();
    789        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
    790            Map.Entry entry = (Map.Entry) iter.next();
    791            String key = entry.getKey().toString();
    792
    793            Object value = entry.getValue();
    794            setProperty(bean, key, value);
    795        }
    796    }
    797
    798    /***
    799     * @return the given property's value on the object
    800     */
    801    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
    802        checkInitalised();
    803
    804        //----------------------------------------------------------------------
    805        // handling of static
    806        //----------------------------------------------------------------------
    807        boolean isStatic = theClass != Class.class && object instanceof Class;
    808        if (isStatic && object != theClass) {
    809            MetaClass mc = registry.getMetaClass((Class) object);
    810            return mc.getProperty(sender,object,name,useSuper,false);
    811        }
    812
    813        MetaMethod method = null;
    814        Object[] arguments = EMPTY_ARGUMENTS;
    815
    816        //----------------------------------------------------------------------
    817        // getter
    818        //----------------------------------------------------------------------
    819        MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
    820        if (mp != null) {
    821            if (mp instanceof MetaBeanProperty) {
    822                MetaBeanProperty mbp = (MetaBeanProperty) mp;
    823                method = mbp.getGetter();
    824                mp = mbp.getField();
    825            }
    826        }
    827
    828        // check for a category method named like a getter
    829        if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
    830            String getterName = "get"+MetaClassHelper.capitalize(name);
    831            method = getCategoryMethodGetter(sender,getterName,false);
    832        }
    833
    834        //----------------------------------------------------------------------
    835        // field
    836        //----------------------------------------------------------------------
    837        if (method==null && mp!=null) {
    838            return mp.getProperty(object);
    839        }
    840
    841
    842        //----------------------------------------------------------------------
    843        // generic get method
    844        //----------------------------------------------------------------------
    845        // check for a generic get method provided through a category
    846        if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
    847            method = getCategoryMethodGetter(sender,"get",true);
    848            if (method!=null) arguments = new Object[]{name};
    849        }
    850
    851        // the generic method is valid, if available (!=null), if static or
    852        // if it is not static and we do no static access
    853        if (method==null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
    854            arguments = new Object[]{ name };
    855            method = genericGetMethod;
    856        }
    857
    858        //----------------------------------------------------------------------
    859        // special cases
    860        //----------------------------------------------------------------------
    861        if (method==null) {
    862            /*** todo these special cases should be special MetaClasses maybe */
    863            if (theClass != Class.class && object instanceof Class) {
    864                MetaClass mc = registry.getMetaClass(Class.class);
    865                return mc.getProperty(Class.class,object,name,useSuper,false);
    866            } else if (object instanceof Collection) {
    867                return DefaultGroovyMethods.getAt((Collection) object, name);
    868            } else if (object instanceof Object[]) {
    869                return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
    870            } else {
    871                MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
    872                if (addListenerMethod != null) {
    873                    //TODO: one day we could try return the previously registered Closure listener for easy removal
    874                    return null;
    875                }
    876            }
    877        } else {
    878
    879            //----------------------------------------------------------------------
    880            // executing the getter method
    881            //----------------------------------------------------------------------
    882            return MetaClassHelper.doMethodInvoke(object,method,arguments);
    883        }
    884
    885        //----------------------------------------------------------------------
    886        // error due to missing method/field
    887        //----------------------------------------------------------------------
    888        throw new MissingPropertyException(name, theClass);
    889    }
    890
    891    private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
    892        List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
    893        if (possibleGenericMethods != null) {
    894            for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
    895                MetaMethod mmethod = (MetaMethod) iter.next();
    896                Class[] paramTypes = mmethod.getParameterTypes();
    897                if (useLongVersion) {
    898                    if (paramTypes.length==1 && paramTypes[0] == String.class) {
    899                        return mmethod;
    900                    }
    901                } else {
    902                    if (paramTypes.length==0) return mmethod;
    903                }
    904            }
    905        }
    906        return null;
    907    }
    908
    909    private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
    910        List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
    911        if (possibleGenericMethods != null) {
    912            for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
    913                MetaMethod mmethod = (MetaMethod) iter.next();
    914                Class[] paramTypes = mmethod.getParameterTypes();
    915                if (useLongVersion) {
    916                    if (paramTypes.length==2 && paramTypes[0] == String.class) {
    917                        return mmethod;
    918                    }
    919                } else {
    920                    if (paramTypes.length==1) return mmethod;
    921                }
    922            }
    923        }
    924        return null;
    925    }
    926
    927    /***
    928     * Get all the properties defined for this type
    929     * @return a list of MetaProperty objects
    930     */
    931    public List getProperties() {
    932        checkInitalised();
    933        Map propertyMap = (Map) classPropertyIndex.get(theClass);
    934        // simply return the values of the metaproperty map as a List
    935        List ret = new ArrayList(propertyMap.size());
    936        for (Iterator iter = propertyMap.values().iterator(); iter.hasNext();) {
    937            MetaProperty element = (MetaProperty) iter.next();
    938            if (element instanceof MetaFieldProperty) continue;
    939            // filter out DGM beans
    940            if (element instanceof MetaBeanProperty) {
    941                MetaBeanProperty mp = (MetaBeanProperty) element;
    942                boolean setter = true;
    943                boolean getter = true;
    944                if (mp.getGetter()==null || mp.getGetter() instanceof NewInstanceMetaMethod) {
    945                    getter=false;
    946                }
    947                if (mp.getSetter()==null || mp.getSetter() instanceof NewInstanceMetaMethod) {
    948                    setter=false;
    949                }
    950                if (!setter && !getter) continue;
    951                if (!setter && mp.getSetter()!=null) {
    952                    element = new MetaBeanProperty(mp.getName(),mp.getType(),mp.getGetter(),null);
    953                }
    954                if (!getter && mp.getGetter()!=null) {
    955                    element = new MetaBeanProperty(mp.getName(),mp.getType(), null, mp.getSetter());
    956                }
    957            }
    958            ret.add(element);
    959        }
    960        return ret;
    961    }
    962
    963    private MetaMethod findPropertyMethod(List methods, boolean isGetter) {
    964        LinkedList ret = new LinkedList();
    965        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    966            MetaMethod element = (MetaMethod) iter.next();
    967            if ( !isGetter &&
    968                 //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
    969                 element.getParameterTypes().length == 1)
    970            {
    971                ret.add(element);
    972            }
    973            if ( isGetter &&
    974                 !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
    975                 element.getParameterTypes().length == 0)
    976            {
    977                ret.add(element);
    978            }
    979        }
    980        if (ret.size() == 0) return null;
    981        if (ret.size() == 1) return (MetaMethod) ret.getFirst();
    982
    983        // we found multiple matching methods
    984        // this is a problem, because we can use only one
    985        // if it is a getter, then use the most general return
    986        // type to decide which method to use. If it is a setter
    987        // we use the type of the first parameter
    988        MetaMethod method = null;
    989        int distance = -1;
    990        for (Iterator iter = ret.iterator(); iter.hasNext();) {
    991            MetaMethod element = (MetaMethod) iter.next();
    992            Class c;
    993            if (isGetter) {
    994                c = element.getReturnType();
    995            } else {
    996                c = element.getParameterTypes()[0];
    997            }
    998            int localDistance = distanceToObject(c);
    999            //TODO: maybe implement the case localDistance==distance
    1000            if (distance==-1 || distance>localDistance) {
    1001                distance = localDistance;
    1002                method = element;
    1003            }
    1004        }
    1005        return method;
    1006    }
    1007
    1008    private static int distanceToObject(Class c) {
    1009        int count;
    1010        for (count=0; c!=null; count++) {
    1011            c=c.getSuperclass();
    1012        }
    1013        return count;
    1014    }
    1015
    1016
    1017    /***
    1018     * This will build up the property map (Map of MetaProperty objects, keyed on
    1019     * property name).
    1020     */
    1021    private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
    1022        LinkedList superClasses = getSuperClasses();
    1023        Set interfaces = new HashSet();
    1024        makeInterfaceSet(theClass,interfaces);
    1025
    1026        // if this an Array, then add the special read-only "length" property
    1027        if (theClass.isArray()) {
    1028            Map map = new HashMap();
    1029            map.put("length", arrayLengthProperty);
    1030            classPropertyIndex.put(theClass,map);
    1031        }
    1032
    1033        inheritStaticInterfaceFields(superClasses, interfaces);
    1034        inheritFields(superClasses);
    1035        applyPropertyDescriptors(propertyDescriptors);
    1036
    1037        applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex);
    1038        applyStrayPropertyMethods(superClasses,classMethodIndexForSuper,classPropertyIndexForSuper);
    1039
    1040        copyClassPropertyIndexForSuper();
    1041        makeStaticPropertyIndex();
    1042    }
    1043
    1044    private void makeStaticPropertyIndex() {
    1045        Map propertyMap = (Map) classPropertyIndex.get(theClass);
    1046        for (Iterator iter = propertyMap.entrySet().iterator(); iter.hasNext();) {
    1047            Map.Entry entry = (Map.Entry) iter.next();
    1048            MetaProperty mp = (MetaProperty) entry.getValue();
    1049            if (mp instanceof MetaFieldProperty) {
    1050                MetaFieldProperty mfp = (MetaFieldProperty) mp;
    1051                if (!mfp.isStatic()) continue;
    1052            } else if (mp instanceof MetaBeanProperty) {
    1053                MetaBeanProperty mbp = (MetaBeanProperty) mp;
    1054                boolean getter = mbp.getGetter()==null || mbp.getGetter().isStatic();
    1055                boolean setter = mbp.getSetter()==null || mbp.getSetter().isStatic();
    1056                boolean field = mbp.getField()==null || mbp.getField().isStatic();
    1057
    1058                if (!getter && !setter && !field) {
    1059                    continue;
    1060                } else if (setter && getter) {
    1061                    if (field) {
    1062                        mp = mbp; // nothing to do
    1063                    } else {
    1064                        mp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),mbp.getSetter());
    1065                    }
    1066                } else if (getter && !setter) {
    1067                    if (mbp.getGetter()==null) {
    1068                        mp = mbp.getField();
    1069                    } else {
    1070                        MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),null);
    1071                        if (field) newmp.setField(mbp.getField());
    1072                        mp = newmp;
    1073                    }
    1074                } else if (setter && !getter) {
    1075                    if (mbp.getSetter()==null) {
    1076                        mp = mbp.getField();
    1077                    } else {
    1078                        MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),null,mbp.getSetter());
    1079                        if (field) newmp.setField(mbp.getField());
    1080                        mp = newmp;
    1081                    }
    1082                } else if (field) {
    1083                    mp = mbp.getField();
    1084                }
    1085            } else {
    1086                continue; // ignore all other types
    1087            }
    1088            if (mp==null) continue;
    1089            staticPropertyIndex.put(entry.getKey(),mp);
    1090        }
    1091
    1092    }
    1093
    1094    private void copyClassPropertyIndexForSuper() {
    1095        for (Iterator iter = classPropertyIndex.entrySet().iterator(); iter.hasNext();) {
    1096            Map.Entry entry = (Map.Entry) iter.next();
    1097            HashMap newVal = new HashMap((Map)entry.getValue());
    1098            classPropertyIndexForSuper.put(entry.getKey(),newVal);
    1099        }
    1100    }
    1101
    1102    private Map getMap2MapNotNull(Map m, Object key) {
    1103        Map ret = (Map) m.get(key);
    1104        if (ret==null) {
    1105            ret = new HashMap();
    1106            m.put(key,ret);
    1107        }
    1108        return ret;
    1109    }
    1110
    1111    private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
    1112        for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
    1113            Class iclass = (Class) interfaceIter.next();
    1114            Map iPropertyIndex = getMap2MapNotNull(classPropertyIndex,iclass);
    1115            addFields(iclass,iPropertyIndex);
    1116            for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
    1117                Class sclass = (Class) classIter.next();
    1118                if (! iclass.isAssignableFrom(sclass)) continue;
    1119                Map sPropertyIndex = getMap2MapNotNull(classPropertyIndex,sclass);
    1120                copyNonPrivateFields(iPropertyIndex,sPropertyIndex);
    1121            }
    1122        }
    1123    }
    1124
    1125    private void inheritFields(LinkedList superClasses) {
    1126        Map last = null;
    1127        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    1128            Class klass = (Class) iter.next();
    1129            Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
    1130            if (last != null) {
    1131                copyNonPrivateFields(last,propertyIndex);
    1132            }
    1133            last = propertyIndex;
    1134            addFields(klass,propertyIndex);
    1135        }
    1136    }
    1137
    1138    private void addFields(final Class klass, Map propertyIndex) {
    1139        Field[] fields = (Field[]) AccessController.doPrivileged(new  PrivilegedAction() {
    1140            public Object run() {
    1141                return klass.getDeclaredFields();
    1142            }
    1143        });
    1144        for(int i = 0; i < fields.length; i++) {
    1145            MetaFieldProperty mfp = new MetaFieldProperty(fields[i]);
    1146            propertyIndex.put(fields[i].getName(), mfp);
    1147        }
    1148    }
    1149
    1150    private void copyNonPrivateFields(Map from, Map to) {
    1151        for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) {
    1152            Map.Entry entry = (Map.Entry) iter.next();
    1153            MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue();
    1154            if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
    1155            to.put(entry.getKey(),mfp);
    1156        }
    1157    }
    1158
    1159    private void applyStrayPropertyMethods(LinkedList superClasses, Map classMethodIndex, Map classPropertyIndex) {
    1160        // now look for any stray getters that may be used to define a property
    1161        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
    1162            Class klass = (Class) iter.next();
    1163            Map methodIndex = (Map) classMethodIndex.get(klass);
    1164            Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
    1165            for (Iterator nameMethodIterator = methodIndex.entrySet().iterator(); nameMethodIterator.hasNext();) {
    1166                Map.Entry entry = (Map.Entry) nameMethodIterator.next();
    1167                String methodName = (String) entry.getKey();
    1168                // name too sort?
    1169                if (methodName.length() < 4) continue;
    1170                //possible getter/setter
    1171                boolean isGetter = methodName.startsWith("get");
    1172                boolean isSetter = methodName.startsWith("set");
    1173                if (!isGetter && !isSetter) continue;
    1174
    1175                // get the name of the property
    1176                String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
    1177                MetaMethod propertyMethod = findPropertyMethod((List) entry.getValue(), isGetter);
    1178                if (propertyMethod==null) continue;
    1179
    1180                createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
    1181            }
    1182        }
    1183    }
    1184
    1185    private void createMetaBeanProperty(Map propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod){
    1186        // is this property already accounted for?
    1187        MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
    1188        if (mp == null) {
    1189            if (isGetter) {
    1190                mp = new MetaBeanProperty(propName,
    1191                        propertyMethod.getReturnType(),
    1192                        propertyMethod, null);
    1193            } else {
    1194                //isSetter
    1195                mp = new MetaBeanProperty(propName,
    1196                        propertyMethod.getParameterTypes()[0],
    1197                        null, propertyMethod);
    1198            }
    1199        } else {
    1200            MetaBeanProperty mbp;
    1201            MetaFieldProperty mfp;
    1202            if (mp instanceof MetaBeanProperty) {
    1203                mbp = (MetaBeanProperty) mp;
    1204                mfp = mbp.getField();
    1205            } else if (mp instanceof MetaFieldProperty){
    1206                mfp = (MetaFieldProperty) mp;
    1207                mbp = new MetaBeanProperty(propName,
    1208                        mfp.getType(),
    1209                        null, null);
    1210            } else {
    1211                throw new GroovyBugError("unknown MetaProperty class used. Class is "+mp.getClass());
    1212            }
    1213            // we may have already found one for this name
    1214            if (isGetter && mbp.getGetter()==null) {
    1215                mbp.setGetter(propertyMethod);
    1216            } else if (!isGetter && mbp.getSetter()==null) {
    1217                mbp.setSetter(propertyMethod);
    1218            }
    1219            mbp.setField(mfp);
    1220            mp = mbp;
    1221        }
    1222        propertyIndex.put(propName, mp);
    1223    }
    1224
    1225    private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
    1226        Map propertyMap = (Map) classPropertyIndex.get(theClass);
    1227        // now iterate over the map of property descriptors and generate
    1228        // MetaBeanProperty objects
    1229        for(int i=0; i<propertyDescriptors.length; i++) {
    1230            PropertyDescriptor pd = propertyDescriptors[i];
    1231
    1232            // skip if the property type is unknown (this seems to be the case if the
    1233            // property descriptor is based on a setX() method that has two parameters,
    1234            // which is not a valid property)
    1235            if(pd.getPropertyType() == null)
    1236                continue;
    1237
    1238            // get the getter method
    1239            Method method = pd.getReadMethod();
    1240            MetaMethod getter;
    1241            if(method != null)
    1242                getter = findMethod(method);
    1243            else
    1244                getter = null;
    1245
    1246            // get the setter method
    1247            MetaMethod setter;
    1248            method = pd.getWriteMethod();
    1249            if(method != null)
    1250                setter = findMethod(method);
    1251            else
    1252                setter = null;
    1253
    1254            // now create the MetaProperty object
    1255            MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
    1256
    1257            //keep field
    1258            MetaFieldProperty field = null;
    1259            MetaProperty old = (MetaProperty) propertyMap.get(pd.getName());
    1260            if (old!=null) {
    1261                if (old instanceof MetaBeanProperty) {
    1262                    field = ((MetaBeanProperty) old).getField();
    1263                } else {
    1264                    field = (MetaFieldProperty) old;
    1265                }
    1266                mp.setField(field);
    1267            }
    1268
    1269            // put it in the list
    1270            // this will overwrite a possible field property
    1271            propertyMap.put(pd.getName(), mp);
    1272        }
    1273    }
    1274
    1275    /***
    1276     * Sets the property value on an object
    1277     */
    1278    public void setProperty(Class sender,Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
    1279        checkInitalised();
    1280
    1281        //----------------------------------------------------------------------
    1282        // handling of static
    1283        //----------------------------------------------------------------------
    1284        boolean isStatic = theClass != Class.class && object instanceof Class;
    1285        if (isStatic && object != theClass) {
    1286            MetaClass mc = registry.getMetaClass((Class) object);
    1287            mc.getProperty(sender,object,name,useSuper,fromInsideClass);
    1288            return;
    1289        }
    1290
    1291        //----------------------------------------------------------------------
    1292        // Unwrap wrapped values fo now - the new MOP will handle them properly
    1293        //----------------------------------------------------------------------
    1294        if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap();
    1295
    1296
    1297
    1298        MetaMethod method = null;
    1299        Object[] arguments = null;
    1300
    1301        //----------------------------------------------------------------------
    1302        // setter
    1303        //----------------------------------------------------------------------
    1304        MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
    1305        MetaProperty field = null;
    1306        if (mp != null) {
    1307            if (mp instanceof MetaBeanProperty) {
    1308                MetaBeanProperty mbp = (MetaBeanProperty) mp;
    1309                method = mbp.getSetter();
    1310                if (method!=null) arguments = new Object[] { newValue };
    1311                field = mbp.getField();
    1312            } else {
    1313                field = mp;
    1314            }
    1315        }
    1316
    1317        // check for a category method named like a setter
    1318        if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
    1319            String getterName = "set"+MetaClassHelper.capitalize(name);
    1320            method = getCategoryMethodSetter(sender,getterName,false);
    1321            if (method!=null) arguments = new Object[] { newValue };
    1322        }
    1323
    1324        //----------------------------------------------------------------------
    1325        // listener method
    1326        //----------------------------------------------------------------------
    1327        boolean ambigousListener = false;
    1328        boolean usesProxy = false;
    1329        if (method==null) {
    1330            method = (MetaMethod) listeners.get(name);
    1331            ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
    1332            if ( method != null &&
    1333                 !ambigousListener &&
    1334                 newValue instanceof Closure)
    1335            {
    1336                // lets create a dynamic proxy
    1337                Object proxy =
    1338                    MetaClassHelper.createListenerProxy(method.getParameterTypes()[0], name, (Closure) newValue);
    1339                arguments = new Object[] { proxy };
    1340                newValue = proxy;
    1341                usesProxy = true;
    1342            } else {
    1343                method = null;
    1344            }
    1345        }
    1346
    1347        //----------------------------------------------------------------------
    1348        // field
    1349        //----------------------------------------------------------------------
    1350        if (method==null && field!=null) {
    1351            field.setProperty(object,newValue);
    1352            return;
    1353        }
    1354
    1355        //----------------------------------------------------------------------
    1356        // generic set method
    1357        //----------------------------------------------------------------------
    1358        // check for a generic get method provided through a category
    1359        if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
    1360            method = getCategoryMethodSetter(sender,"set",true);
    1361            if (method!=null) arguments = new Object[]{name,newValue};
    1362        }
    1363
    1364        // the generic method is valid, if available (!=null), if static or
    1365        // if it is not static and we do no static access
    1366        if (method==null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
    1367            arguments = new Object[]{ name, newValue };
    1368            method = genericSetMethod;
    1369        }
    1370
    1371        //----------------------------------------------------------------------
    1372        // executing the getter method
    1373        //----------------------------------------------------------------------
    1374        if (method!=null) {
    1375            if (arguments.length==1) {
    1376                newValue = DefaultTypeTransformation.castToType(
    1377                        newValue,
    1378                        method.getParameterTypes()[0]);
    1379                arguments[0] = newValue;
    1380            } else {
    1381                newValue = DefaultTypeTransformation.castToType(
    1382                        newValue,
    1383                        method.getParameterTypes()[1]);
    1384                arguments[1] = newValue;
    1385            }
    1386            MetaClassHelper.doMethodInvoke(object,method,arguments);
    1387            return;
    1388        }
    1389
    1390        //----------------------------------------------------------------------
    1391        // error due to missing method/field
    1392        //----------------------------------------------------------------------
    1393        if (ambigousListener){
    1394            throw new GroovyRuntimeException("There are multiple listeners for the property "+name+". Please do not use the bean short form to access this listener.");
    1395        }
    1396        throw new MissingPropertyException(name, theClass);
    1397    }
    1398
    1399    private MetaProperty getMetaProperty(Class clazz, String name, boolean useSuper, boolean useStatic) {
    1400        Map propertyMap;
    1401        if (useStatic) {
    1402            propertyMap = staticPropertyIndex;
    1403        } else if (useSuper){
    1404            propertyMap = (Map) classPropertyIndexForSuper.get(clazz);
    1405        } else {
    1406            propertyMap = (Map) classPropertyIndex.get(clazz);
    1407        }
    1408        if (propertyMap==null) {
    1409            if (clazz!=theClass) {
    1410                return getMetaProperty(theClass,name,useSuper, useStatic);
    1411            } else {
    1412                return null;
    1413            }
    1414        }
    1415        return (MetaProperty) propertyMap.get(name);
    1416    }
    1417
    1418
    1419    /***
    1420     * Looks up the given attribute (field) on the given object
    1421     */
    1422    public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
    1423        checkInitalised();
    1424
    1425        boolean isStatic = theClass != Class.class && object instanceof Class;
    1426        if (isStatic && object != theClass) {
    1427            MetaClass mc = registry.getMetaClass((Class) object);
    1428            return mc.getAttribute(sender,object,attribute,useSuper);
    1429        }
    1430
    1431        MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
    1432
    1433        if (mp != null) {
    1434            if (mp instanceof MetaBeanProperty) {
    1435                MetaBeanProperty mbp = (MetaBeanProperty) mp;
    1436                mp = mbp.getField();
    1437            }
    1438            try {
    1439                // delegate the get operation to the metaproperty
    1440                if (mp != null) return mp.getProperty(object);
    1441            } catch(Exception e) {
    1442                throw new GroovyRuntimeException("Cannot read field: " + attribute,e);
    1443            }
    1444        }
    1445
    1446        throw new MissingFieldException(attribute, theClass);
    1447    }
    1448
    1449    /***
    1450     * Sets the given attribute (field) on the given object
    1451     */
    1452    public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
    1453        checkInitalised();
    1454
    1455        boolean isStatic = theClass != Class.class && object instanceof Class;
    1456        if (isStatic && object != theClass) {
    1457            MetaClass mc = registry.getMetaClass((Class) object);
    1458            mc.setAttribute(sender,object,attribute,newValue,useSuper,fromInsideClass);
    1459            return;
    1460        }
    1461
    1462        MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
    1463
    1464        if (mp != null) {
    1465            if (mp instanceof MetaBeanProperty) {
    1466                MetaBeanProperty mbp = (MetaBeanProperty) mp;
    1467                mp = mbp.getField();
    1468            }
    1469            if (mp != null) {
    1470                mp.setProperty(object,newValue);
    1471                return;
    1472            }
    1473        }
    1474
    1475        throw new MissingFieldException(attribute, theClass);
    1476    }
    1477
    1478    public ClassNode getClassNode() {
    1479        if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
    1480            // lets try load it from the classpath
    1481            String className = theClass.getName();
    1482            String groovyFile = className;
    1483            int idx = groovyFile.indexOf('$');
    1484            if (idx > 0) {
    1485                groovyFile = groovyFile.substring(0, idx);
    1486            }
    1487            groovyFile = groovyFile.replace('.', '/') + ".groovy";
    1488
    1489            //System.out.println("Attempting to load: " + groovyFile);
    1490            URL url = theClass.getClassLoader().getResource(groovyFile);
    1491            if (url == null) {
    1492                url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
    1493            }
    1494            if (url != null) {
    1495                try {
    1496
    1497                    /***
    1498                     * todo there is no CompileUnit in scope so class name
    1499                     * checking won't work but that mostly affects the bytecode
    1500                     * generation rather than viewing the AST
    1501                     */
    1502                    CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
    1503                        public void call( ClassVisitor writer, ClassNode node ) {
    1504                            if( node.getName().equals(theClass.getName()) ) {
    1505                                MetaClassImpl.this.classNode = node;
    1506                            }
    1507                        }
    1508                    };
    1509
    1510                    final ClassLoader parent = theClass.getClassLoader();
    1511                    GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
    1512                        public Object run() {
    1513                            return new GroovyClassLoader(parent);
    1514                        }
    1515                    });
    1516                    CompilationUnit unit = new CompilationUnit( );
    1517                    unit.setClassgenCallback( search );
    1518                    unit.addSource( url );
    1519                    unit.compile( Phases.CLASS_GENERATION );
    1520                }
    1521                catch (Exception e) {
    1522                    throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
    1523                }
    1524            }
    1525
    1526        }
    1527        return classNode;
    1528    }
    1529
    1530    public String toString() {
    1531        return super.toString() + "[" + theClass + "]";
    1532    }
    1533
    1534    // Implementation methods
    1535    //-------------------------------------------------------------------------
    1536
    1537    /***
    1538     * Adds all the methods declared in the given class to the metaclass
    1539     * ignoring any matching methods already defined by a derived class
    1540     *
    1541     * @param theClass
    1542     */
    1543    private void addMethods(final Class theClass) {
    1544        Map methodIndex = (Map) classMethodIndex.get(theClass);
    1545        if (methodIndex==null) {
    1546            methodIndex = new HashMap();
    1547            classMethodIndex.put(theClass,methodIndex);
    1548        }
    1549        // add methods directly declared in the class
    1550        Method[] methodArray = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
    1551                public Object run() {
    1552                    return theClass.getDeclaredMethods();
    1553                }
    1554            });
    1555        for (int i = 0; i < methodArray.length; i++) {
    1556            Method reflectionMethod = methodArray[i];
    1557            if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
    1558                // Skip Synthetic methods inserted by JDK 1.5 compilers and later
    1559                continue;
    1560            } else if (Modifier.isAbstract(reflectionMethod.getModifiers())) {
    1561                continue;
    1562            }
    1563            MetaMethod method = createMetaMethod(reflectionMethod);
    1564            addMetaMethod(method);
    1565        }
    1566        // add methods declared by DGM
    1567        List methods = registry.getInstanceMethods();
    1568        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1569            Method element = (Method) iter.next();
    1570            if (element.getParameterTypes()[0]!=theClass) continue;
    1571            addNewInstanceMethod(element);
    1572        }
    1573        // add static methods declared by DGM
    1574        methods = registry.getStaticMethods();
    1575        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1576            Method element = (Method) iter.next();
    1577            if (element.getParameterTypes()[0]!=theClass) continue;
    1578            addNewStaticMethod(element);
    1579        }
    1580    }
    1581
    1582    private void addToClassMethodIndex(MetaMethod method, Map classMethodIndex) {
    1583        Map methodIndex = (Map) classMethodIndex.get(method.getDeclaringClass());
    1584        if (methodIndex==null) {
    1585            methodIndex = new HashMap();
    1586            classMethodIndex.put(method.getDeclaringClass(),methodIndex);
    1587        }
    1588        String name = method.getName();
    1589        List list = (List) methodIndex.get(name);
    1590        if (list == null) {
    1591            list = new ArrayList();
    1592            methodIndex.put(name, list);
    1593            list.add(method);
    1594        } else {
    1595            addMethodToList(list,method);
    1596        }
    1597    }
    1598
    1599    /***
    1600     * adds a MetaMethod to this class. WARNING: this method will not
    1601     * do the neccessary steps for multimethod logic and using this
    1602     * method doesn't mean, that a method added here is replacing another
    1603     * method from a parent class completely. These steps are usually done
    1604     * by initalize, which means if you need these steps, you have to add
    1605     * the method before running initialize the first time.
    1606     * @see #initialize()
    1607     * @param method the MetaMethod
    1608     */
    1609    protected void addMetaMethod(MetaMethod method) {
    1610        if (isInitialized()) {
    1611            throw new RuntimeException("Already initialized, cannot add new method: " + method);
    1612        }
    1613        if (isGenericGetMethod(method) && genericGetMethod == null) {
    1614            genericGetMethod = method;
    1615        }
    1616        else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
    1617            genericSetMethod = method;
    1618        }
    1619        if (method.isStatic()) {
    1620            addToClassMethodIndex(method,classStaticMethodIndex);
    1621        }
    1622        addToClassMethodIndex(method,classMethodIndex);
    1623    }
    1624
    1625    protected boolean isInitialized(){
    1626        return initialized;
    1627    }
    1628
    1629    private void addMethodToList(List list, MetaMethod method) {
    1630        MetaMethod match = removeMatchingMethod(list,method);
    1631        if (match==null) {
    1632            list.add(method);
    1633        } else if (match.isPrivate()){
    1634            // do not overwrite private methods
    1635            // Note: private methods from parent classes are not shown here,
    1636            // but when doing the multimethod connection step, we overwrite
    1637            // methods of the parent class with methods of a subclass and
    1638            // in that case we want to keep the private methods
    1639            list.add(match);
    1640        } else {
    1641            Class methodC = method.getDeclaringClass();
    1642            Class matchC = match.getDeclaringClass();
    1643            if (methodC == matchC){
    1644                if (method instanceof NewInstanceMetaMethod) {
    1645                    // let DGM replace existing methods
    1646                    list.add(method);
    1647                } else {
    1648                    list.add(match);
    1649                }
    1650            } else if (MetaClassHelper.isAssignableFrom(methodC,matchC)){
    1651                list.add(match);
    1652            } else {
    1653               list.add(method);
    1654            }
    1655        }
    1656    }
    1657
    1658    /***
    1659     * remove a method of the same matching prototype was found in the list
    1660     */
    1661    private MetaMethod removeMatchingMethod(List list, MetaMethod method) {
    1662        for (Iterator iter = list.iterator(); iter.hasNext();) {
    1663            MetaMethod aMethod = (MetaMethod) iter.next();
    1664            Class[] params1 = aMethod.getParameterTypes();
    1665            Class[] params2 = method.getParameterTypes();
    1666            if (params1.length == params2.length) {
    1667                boolean matches = true;
    1668                for (int i = 0; i < params1.length; i++) {
    1669                    if (params1[i] != params2[i]) {
    1670                        matches = false;
    1671                        break;
    1672                    }
    1673                }
    1674                if (matches) {
    1675                    iter.remove();
    1676                    return (MetaMethod) aMethod;
    1677                }
    1678            }
    1679        }
    1680        return null;
    1681    }
    1682
    1683    /***
    1684     * @return the matching method which should be found
    1685     */
    1686    private MetaMethod findMethod(Method aMethod) {
    1687        List methods = getMethods(theClass,aMethod.getName(),false);
    1688        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1689            MetaMethod method = (MetaMethod) iter.next();
    1690            if (method.isMethod(aMethod)) {
    1691                return method;
    1692            }
    1693        }
    1694        //log.warning("Creating reflection based dispatcher for: " + aMethod);
    1695        return new ReflectionMetaMethod(aMethod);
    1696    }
    1697
    1698    /***
    1699     * @return the getter method for the given object
    1700     */
    1701    private MetaMethod findGetter(Object object, String name) {
    1702        List methods = getMethods(theClass,name,false);
    1703        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1704            MetaMethod method = (MetaMethod) iter.next();
    1705            if (method.getParameterTypes().length == 0) {
    1706                return method;
    1707            }
    1708        }
    1709        return null;
    1710    }
    1711
    1712    /***
    1713     * @return the Method of the given name with no parameters or null
    1714     */
    1715    private MetaMethod findStaticGetter(Class type, String name) {
    1716        List methods = getStaticMethods(type, name);
    1717        for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1718            MetaMethod method = (MetaMethod) iter.next();
    1719            if (method.getParameterTypes().length == 0) {
    1720                return method;
    1721            }
    1722        }
    1723
    1724        /*** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
    1725        try {
    1726            Method method = type.getMethod(name, MetaClassHelper.EMPTY_TYPE_ARRAY);
    1727            if ((method.getModifiers() & Modifier.STATIC) != 0) {
    1728                return findMethod(method);
    1729            }
    1730            else {
    1731                return null;
    1732            }
    1733        }
    1734        catch (Exception e) {
    1735            return null;
    1736        }
    1737    }
    1738
    1739    private static Object doConstructorInvoke(final Class at, Constructor constructor, Object[] argumentArray, boolean setAccessible) {
    1740        if (log.isLoggable(Level.FINER)) {
    1741            MetaClassHelper.logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
    1742        }
    1743
    1744        if (setAccessible) {
    1745            // To fix JIRA 435
    1746            // Every constructor should be opened to the accessible classes.
    1747            final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
    1748            final Constructor ctor = constructor;
    1749            AccessController.doPrivileged(new PrivilegedAction() {
    1750                public Object run() {
    1751                    ctor.setAccessible(accessible);
    1752                    return null;
    1753                }
    1754            });
    1755        }
    1756        return MetaClassHelper.doConstructorInvoke(constructor,argumentArray);
    1757    }
    1758
    1759    /***
    1760     * Chooses the correct method to use from a list of methods which match by
    1761     * name.
    1762     *
    1763     * @param methods
    1764     *            the possible methods to choose from
    1765     * @param arguments
    1766     *            the original argument to the method
    1767     */
    1768    private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
    1769        int methodCount = methods.size();
    1770        if (methodCount <= 0) {
    1771            return null;
    1772        }
    1773        else if (methodCount == 1) {
    1774            Object method = methods.get(0);
    1775            if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
    1776                return method;
    1777            }
    1778            return null;
    1779        }
    1780        Object answer = null;
    1781        if (arguments == null || arguments.length == 0) {
    1782            answer = MetaClassHelper.chooseEmptyMethodParams(methods);
    1783        }
    1784        else if (arguments.length == 1 && arguments[0] == null) {
    1785            answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
    1786        }
    1787        else {
    1788            List matchingMethods = new ArrayList();
    1789
    1790            for (Iterator iter = methods.iterator(); iter.hasNext();) {
    1791                Object method = iter.next();
    1792
    1793                // making this false helps find matches
    1794                if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
    1795                    matchingMethods.add(method);
    1796                }
    1797            }
    1798            if (matchingMethods.isEmpty()) {
    1799                return null;
    1800            }
    1801            else if (matchingMethods.size() == 1) {
    1802                return matchingMethods.get(0);
    1803            }
    1804            return chooseMostSpecificParams(methodName, matchingMethods, arguments);
    1805
    1806        }
    1807        if (answer != null) {
    1808            return answer;
    1809        }
    1810        throw new GroovyRuntimeException(
    1811            "Could not find which method to invoke from this list: "
    1812                + methods
    1813                + " for arguments: "
    1814                + InvokerHelper.toString(arguments));
    1815    }
    1816
    1817    private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
    1818
    1819        long matchesDistance = -1;
    1820        LinkedList matches = new LinkedList();
    1821        for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
    1822            Object method = iter.next();
    1823            Class[] paramTypes = MetaClassHelper.getParameterTypes(method);
    1824            if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
    1825            long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
    1826            if (dist==0) return method;
    1827            if (matches.size()==0) {
    1828                matches.add(method);
    1829                matchesDistance = dist;
    1830            } else if (dist<matchesDistance) {
    1831                matchesDistance=dist;
    1832                matches.clear();
    1833                matches.add(method);
    1834            } else if (dist==matchesDistance) {
    1835                matches.add(method);
    1836            }
    1837
    1838        }
    1839        if (matches.size()==1) {
    1840            return matches.getFirst();
    1841        }
    1842        if (matches.size()==0) {
    1843            return null;
    1844        }
    1845
    1846        //more than one matching method found --> ambigous!
    1847        String msg = "Ambiguous method overloading for method ";
    1848        msg+= theClass.getName()+"#"+name;
    1849        msg+= ".\nCannot resolve which method to invoke for ";
    1850        msg+= InvokerHelper.toString(arguments);
    1851        msg+= " due to overlapping prototypes between:";
    1852        for (Iterator iter = matches.iterator(); iter.hasNext();) {
    1853            Class[] types=MetaClassHelper.getParameterTypes(iter.next());
    1854            msg+= "\n\t"+InvokerHelper.toString(types);
    1855        }
    1856        throw new GroovyRuntimeException(msg);
    1857    }
    1858
    1859    private boolean isGenericGetMethod(MetaMethod method) {
    1860        if (method.getName().equals("get")) {
    1861            Class[] parameterTypes = method.getParameterTypes();
    1862            return parameterTypes.length == 1 && parameterTypes[0] == String.class;
    1863        }
    1864        return false;
    1865    }
    1866
    1867    /***
    1868     * Call this method when any mutation method is called, such as adding a new
    1869     * method to this MetaClass so that any caching or bytecode generation can be
    1870     * regenerated.
    1871     */
    1872    private synchronized void onMethodChange() {
    1873        reflector = null;
    1874    }
    1875
    1876
    1877    public synchronized void initialize() {
    1878        if (!isInitialized()) {
    1879            fillMethodIndex();
    1880            addProperties();
    1881            initialized = true;
    1882        }
    1883        if (reflector == null) {
    1884            generateReflector();
    1885        }
    1886    }
    1887
    1888    private void addProperties()  {
    1889        BeanInfo info;
    1890        //     introspect
    1891        try {
    1892            info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1893                public Object run() throws IntrospectionException {
    1894                    return Introspector.getBeanInfo(theClass);
    1895                }
    1896            });
    1897        } catch (PrivilegedActionException pae) {
    1898            throw new GroovyRuntimeException("exception while bean introspection",pae.getException());
    1899        }
    1900        PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
    1901
    1902        // build up the metaproperties based on the public fields, property descriptors,
    1903        // and the getters and setters
    1904        setupProperties(descriptors);
    1905
    1906        EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
    1907        for (int i = 0; i < eventDescriptors.length; i++) {
    1908            EventSetDescriptor descriptor = eventDescriptors[i];
    1909            Method[] listenerMethods = descriptor.getListenerMethods();
    1910            for (int j = 0; j < listenerMethods.length; j++) {
    1911                Method listenerMethod = listenerMethods[j];
    1912                MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
    1913                String name = listenerMethod.getName();
    1914                if (listeners.containsKey(name)) {
    1915                    listeners.put(name, AMBIGOUS_LISTENER_METHOD);
    1916                } else{
    1917                    listeners.put(name, metaMethod);
    1918                }
    1919            }
    1920        }
    1921    }
    1922
    1923    private MetaMethod createMetaMethod(final Method method) {
    1924        if (registry.useAccessible()) {
    1925            AccessController.doPrivileged(new PrivilegedAction() {
    1926                public Object run() {
    1927                    method.setAccessible(true);
    1928                    return null;
    1929                }
    1930            });
    1931        }
    1932
    1933        MetaMethod answer = new MetaMethod(method);
    1934        if (isValidReflectorMethod(answer)) {
    1935            allMethods.add(answer);
    1936            answer.setMethodIndex(allMethods.size());
    1937        }
    1938        else {
    1939            //log.warning("Creating reflection based dispatcher for: " + method);
    1940            answer = new ReflectionMetaMethod(method);
    1941        }
    1942
    1943        if (useReflection) {
    1944            //log.warning("Creating reflection based dispatcher for: " + method);
    1945            return new ReflectionMetaMethod(method);
    1946        }
    1947
    1948        return answer;
    1949    }
    1950
    1951    private boolean isValidReflectorMethod(MetaMethod method) {
    1952        // We cannot use a reflector if the method is private, protected, or package accessible only.
    1953        if (!method.isPublic()) {
    1954            return false;
    1955        }
    1956        // lets see if this method is implemented on an interface
    1957        List interfaceMethods = getInterfaceMethods();
    1958        for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) {
    1959            MetaMethod aMethod = (MetaMethod) iter.next();
    1960            if (method.isSame(aMethod)) {
    1961                method.setInterfaceClass(aMethod.getCallClass());
    1962                return true;
    1963            }
    1964        }
    1965        // it's no interface method, so try to find the highest class
    1966        // in hierarchy defining this method
    1967        Class declaringClass = method.getCallClass();
    1968        for (Class clazz=declaringClass; clazz!=null; clazz=clazz.getSuperclass()) {
    1969            try {
    1970                final Class klazz = clazz;
    1971                final String mName = method.getName();
    1972                final Class[] parms = method.getParameterTypes();
    1973                try {
    1974                    Method m = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
    1975                        public Object run() throws NoSuchMethodException {
    1976                            return klazz.getDeclaredMethod(mName, parms);
    1977                        }
    1978                    });
    1979                    if (!Modifier.isPublic(clazz.getModifiers())) continue;
    1980                    if (!Modifier.isPublic(m.getModifiers())) continue;
    1981                    declaringClass = clazz;
    1982                } catch (PrivilegedActionException pae) {
    1983                    if (pae.getException() instanceof NoSuchMethodException) {
    1984                        throw (NoSuchMethodException) pae.getException();
    1985                    } else {
    1986                        throw new RuntimeException(pae.getException());
    1987                    }
    1988                }
    1989            } catch (SecurityException e) {
    1990                continue;
    1991            } catch (NoSuchMethodException e) {
    1992                continue;
    1993            }
    1994        }
    1995        if (!Modifier.isPublic(declaringClass.getModifiers())) return false;
    1996        method.setCallClass(declaringClass);
    1997
    1998        return true;
    1999    }
    2000
    2001    private void generateReflector() {
    2002        reflector = registry.loadReflector(theClass, allMethods);
    2003        if (reflector == null) {
    2004            throw new RuntimeException("Should have a reflector for "+theClass.getName());
    2005        }
    2006        // lets set the reflector on all the methods
    2007        for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
    2008            MetaMethod metaMethod = (MetaMethod) iter.next();
    2009            metaMethod.setReflector(reflector);
    2010        }
    2011    }
    2012
    2013    public List getMethods() {
    2014        return allMethods;
    2015    }
    2016
    2017    public List getMetaMethods() {
    2018        return new ArrayList(newGroovyMethodsList);
    2019    }
    2020
    2021    private synchronized List getInterfaceMethods() {
    2022        if (interfaceMethods == null) {
    2023            interfaceMethods = new ArrayList();
    2024            Class type = theClass;
    2025            while (type != null) {
    2026                Class[] interfaces = type.getInterfaces();
    2027                for (int i = 0; i < interfaces.length; i++) {
    2028                    Class iface = interfaces[i];
    2029                    Method[] methods = iface.getMethods();
    2030                    addInterfaceMethods(interfaceMethods, methods);
    2031                }
    2032                type = type.getSuperclass();
    2033            }
    2034        }
    2035        return interfaceMethods;
    2036    }
    2037
    2038    private void addInterfaceMethods(List list, Method[] methods) {
    2039        for (int i = 0; i < methods.length; i++) {
    2040            list.add(createMetaMethod(methods[i]));
    2041        }
    2042    }
    2043
    2044    private static class MethodIndexAction {
    2045        public void iterate(Map classMethodIndex){
    2046            for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) {
    2047                Map.Entry classEntry = (Map.Entry) iter.next();
    2048                Map methodIndex = (Map) classEntry.getValue();
    2049                Class clazz = (Class) classEntry.getKey();
    2050                if (skipClass(clazz)) continue;
    2051                for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
    2052                    Map.Entry nameEntry = (Map.Entry) iterator.next();
    2053                    String name = (String) nameEntry.getKey();
    2054                    List oldList = (List) nameEntry.getValue();
    2055                    List newList = methodNameAction(clazz, name, oldList);
    2056                    if (replaceMethodList()) nameEntry.setValue(newList);
    2057                }
    2058            }
    2059        }
    2060        public List methodNameAction(Class clazz, String methodName, List methods) {
    2061            List newList = new ArrayList(methods.size());
    2062            for (Iterator methodIter = methods.iterator(); methodIter.hasNext();) {
    2063                MetaMethod method = (MetaMethod) methodIter.next();
    2064                methodListAction(clazz,methodName,method,methods,newList);
    2065            }
    2066            return newList;
    2067        }
    2068        public boolean skipClass(Class clazz) {return false;}
    2069        public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {}
    2070        public boolean replaceMethodList(){return true;}
    2071    }
    2072
    2073    /***
    2074     * @deprecated
    2075     */
    2076    public Object getProperty(Object object, String property) {
    2077        return getProperty(theClass,object,property,false,false);
    2078    }
    2079
    2080    /***
    2081     * @deprecated
    2082     */
    2083    public void setProperty(Object object, String property, Object newValue) {
    2084        setProperty(theClass,object,property,newValue,false,false);
    2085    }
    2086
    2087    /***
    2088     * @deprecated
    2089     */
    2090    public Object getAttribute(Object object, String attribute) {
    2091        return getAttribute(theClass,object,attribute,false,false);
    2092    }
    2093
    2094    /***
    2095     * @deprecated
    2096     */
    2097    public void setAttribute(Object object, String attribute, Object newValue) {
    2098        setAttribute(theClass,object,attribute,newValue,false,false);
    2099    }
    2100
    2101    public MetaMethod pickMethod(String methodName, Class[] arguments) {
    2102        return getMethodWithoutCaching(theClass,methodName,arguments,false);
    2103    }
    2104
    2105    /***
    2106     * @deprecated use pickMethod instead
    2107     */
    2108    protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
    2109        return pickMethod(methodName,arguments);
    2110    }
    2111
    2112    /***
    2113     * remove all method call cache entries. This should be done if a
    2114     * method is added during runtime, but not by using a category.
    2115     */
    2116    protected void clearInvocationCaches() {
    2117        staticMethodCache.clear();
    2118        methodCache.clear();
    2119    }
    2120 }