View Javadoc

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