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 }