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 c