View Javadoc

1   /*
2    $Id: MetaClass.java,v 1.103 2005/04/01 07:00:59 jez 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.InvocationHandler;
57  import java.lang.reflect.InvocationTargetException;
58  import java.lang.reflect.Method;
59  import java.lang.reflect.Modifier;
60  import java.lang.reflect.Proxy;
61  import java.math.BigDecimal;
62  import java.math.BigInteger;
63  import java.net.URL;
64  import java.security.AccessControlException;
65  import java.security.AccessController;
66  import java.security.PrivilegedAction;
67  import java.security.PrivilegedActionException;
68  import java.security.PrivilegedExceptionAction;
69  import java.util.ArrayList;
70  import java.util.Arrays;
71  import java.util.Collection;
72  import java.util.Collections;
73  import java.util.HashMap;
74  import java.util.Iterator;
75  import java.util.LinkedList;
76  import java.util.List;
77  import java.util.Map;
78  import java.util.logging.Logger;
79  
80  import org.codehaus.groovy.ast.ClassNode;
81  import org.codehaus.groovy.classgen.ReflectorGenerator;
82  import org.codehaus.groovy.control.CompilationUnit;
83  import org.codehaus.groovy.control.Phases;
84  import org.codehaus.groovy.control.CompilerConfiguration;
85  import org.codehaus.groovy.runtime.ClosureListener;
86  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
87  import org.codehaus.groovy.runtime.GroovyCategorySupport;
88  import org.codehaus.groovy.runtime.InvokerHelper;
89  import org.codehaus.groovy.runtime.InvokerInvocationException;
90  import org.codehaus.groovy.runtime.MethodClosure;
91  import org.codehaus.groovy.runtime.MethodHelper;
92  import org.codehaus.groovy.runtime.MethodKey;
93  import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
94  import org.codehaus.groovy.runtime.NewStaticMetaMethod;
95  import org.codehaus.groovy.runtime.ReflectionMetaMethod;
96  import org.codehaus.groovy.runtime.Reflector;
97  import org.codehaus.groovy.runtime.TemporaryMethodKey;
98  import org.codehaus.groovy.runtime.TransformMetaMethod;
99  import org.objectweb.asm.ClassVisitor;
100 import org.objectweb.asm.ClassWriter;
101 
102 /***
103  * Allows methods to be dynamically added to existing classes at runtime
104  * 
105  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
106  * @author Guillaume Laforge
107  * @version $Revision: 1.103 $
108  */
109 public class MetaClass {
110 
111     private static final Logger log = Logger.getLogger(MetaClass.class.getName());
112 
113     public static final Object[] EMPTY_ARRAY = {
114     };
115     public static Class[] EMPTY_TYPE_ARRAY = {
116     };
117     protected static final Object[] ARRAY_WITH_NULL = { null };
118 
119     private static boolean useReflection = false;
120 
121     private MetaClassRegistry registry;
122     private Class theClass;
123     private ClassNode classNode;
124     private Map methodIndex = new HashMap();
125     private Map staticMethodIndex = new HashMap();
126     private List newGroovyMethodsList = new ArrayList();
127     //private Map propertyDescriptors = Collections.synchronizedMap(new HashMap());
128     private Map propertyMap = Collections.synchronizedMap(new HashMap());
129     private Map listeners = new HashMap();
130     private Map methodCache = Collections.synchronizedMap(new HashMap());
131     private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
132     private MetaMethod genericGetMethod;
133     private MetaMethod genericSetMethod;
134     private List constructors;
135     private List allMethods = new ArrayList();
136     private List interfaceMethods;
137     private Reflector reflector;
138     private boolean initialised;
139 	// we only need one of these that can be reused over and over.
140 	private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
141 	
142     public MetaClass(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
143         this.registry = registry;
144         this.theClass = theClass;
145 
146         constructors = Arrays.asList(theClass.getDeclaredConstructors());
147         addMethods(theClass,true);
148 
149         // introspect
150         BeanInfo info = null;
151         try {
152             info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
153                 public Object run() throws IntrospectionException {
154                     return Introspector.getBeanInfo(theClass);
155                 }
156             });
157         } catch (PrivilegedActionException pae) {
158             if (pae.getException() instanceof IntrospectionException) {
159                 throw (IntrospectionException) pae.getException();
160             } else {
161                 throw new RuntimeException(pae.getException());
162             }
163         }
164 
165         PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
166         
167         // build up the metaproperties based on the public fields, property descriptors,
168         // and the getters and setters
169         setupProperties(descriptors);
170         
171         /* old code
172         for (int i = 0; i < descriptors.length; i++) {
173             PropertyDescriptor descriptor = descriptors[i];
174             propertyDescriptors.put(descriptor.getName(), descriptor);
175         }
176         */
177         
178         EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
179         for (int i = 0; i < eventDescriptors.length; i++) {
180             EventSetDescriptor descriptor = eventDescriptors[i];
181             Method[] listenerMethods = descriptor.getListenerMethods();
182             for (int j = 0; j < listenerMethods.length; j++) {
183                 Method listenerMethod = listenerMethods[j];
184                 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
185                 listeners.put(listenerMethod.getName(), metaMethod);
186             }
187         }
188     }
189 
190     public static boolean isUseReflection() {
191         return useReflection;
192     }
193 
194     /***
195      * Allows reflection to be enabled in situations where bytecode generation
196      * of method invocations causes issues.
197      * 
198      * @param useReflection
199      */
200     public static void setUseReflection(boolean useReflection) {
201         MetaClass.useReflection = useReflection;
202     }
203 
204     private void addInheritedMethods() {
205         LinkedList superClasses = new LinkedList();
206         for (Class c = theClass.getSuperclass(); c!=Object.class && c!= null; c = c.getSuperclass()) {
207             superClasses.addFirst(c);
208         }
209         // lets add all the base class methods
210         for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
211             Class c = (Class) iter.next();
212             addMethods(c,true);
213             addNewStaticMethodsFrom(c);
214         }
215 
216         // now lets see if there are any methods on one of my interfaces
217         Class[] interfaces = theClass.getInterfaces();
218         for (int i = 0; i < interfaces.length; i++) {
219             addNewStaticMethodsFrom(interfaces[i]);
220         }
221 
222         // lets add Object methods after interfaces, as all interfaces derive from Object. 
223         // this ensures List and Collection methods come before Object etc
224         if (theClass != Object.class) {
225             addMethods(Object.class, false);
226             addNewStaticMethodsFrom(Object.class);
227         }
228 
229         if (theClass.isArray() && !theClass.equals(Object[].class)) {
230             addNewStaticMethodsFrom(Object[].class);
231         }
232     }
233 
234     /***
235      * @return all the normal instance methods avaiable on this class for the
236      *         given name
237      */
238     public List getMethods(String name) {
239         List answer = (List) methodIndex.get(name);
240         List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
241         if (used != null) {
242             if (answer != null) {
243                 answer.addAll(used);
244             } else{
245                 answer = used;
246             }
247         }
248         if (answer == null) {
249             answer = Collections.EMPTY_LIST;
250         }
251         return answer;
252     }
253 
254     /***
255      * @return all the normal static methods avaiable on this class for the
256      *         given name
257      */
258     public List getStaticMethods(String name) {
259         List answer = (List) staticMethodIndex.get(name);
260         if (answer == null) {
261             return Collections.EMPTY_LIST;
262         }
263         return answer;
264     }
265 
266     /***
267      * Allows static method definitions to be added to a meta class as if it
268      * was an instance method
269      * 
270      * @param method
271      */
272     protected void addNewInstanceMethod(Method method) {
273         if (initialised) {
274             throw new RuntimeException("Already initialized, cannot add new method: " + method);
275         }
276         else {
277             NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
278             addMethod(newMethod,false);
279             addNewInstanceMethod(newMethod);
280         }
281     }
282 
283     protected void addNewInstanceMethod(MetaMethod method) {
284         newGroovyMethodsList.add(method);
285     }
286 
287     protected void addNewStaticMethod(Method method) {
288         if (initialised) {
289             throw new RuntimeException("Already initialized, cannot add new method: " + method);
290         }
291         else {
292             NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
293             addMethod(newMethod,false);
294             addNewStaticMethod(newMethod);
295         }
296     }
297 
298     protected void addNewStaticMethod(MetaMethod method) {
299         newGroovyMethodsList.add(method);
300     }
301 
302     public Object invokeMethod(Object object, String methodName, Object arguments) {
303         return invokeMethod(object, methodName, asArray(arguments));
304     }
305 
306     /***
307      * Invokes the given method on the object.
308      *  
309      */
310     public Object invokeMethod(Object object, String methodName, Object[] arguments) {
311         if (object == null) {
312             throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
313         }
314 
315         MetaMethod method = retrieveMethod(object, methodName, arguments);
316 
317         if (method != null) {
318             return doMethodInvoke(object, method, arguments);
319         } else {
320             // if no method was found, try to find a closure defined as a field of the class and run it
321             try {
322                 Object value = this.getProperty(object, methodName);
323                 if (value instanceof Closure && object!=this) {
324                     Closure closure = (Closure) value;
325                     closure.setDelegate(this);
326                     return closure.call(arguments);
327                 }
328                 else {
329                     throw new MissingMethodException(methodName, theClass, arguments);
330                 }
331             }
332             catch (Exception e) {
333                 throw new MissingMethodException(methodName, theClass, arguments);
334             }
335         }
336     }
337 
338     protected MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
339         // lets try use the cache to find the method
340         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
341         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
342         if (method == null) {
343             method = pickMethod(owner, methodName, arguments);
344             if (method != null && method.isCacheable()) {
345                 methodCache.put(methodKey.createCopy(), method);
346             }
347         }
348         return method;
349     }
350 
351     public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
352         // lets try use the cache to find the method
353         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
354         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
355         if (method == null) {
356             method = pickMethod(methodName, arguments); // todo shall call pickStaticMethod also?
357             if (method != null && method.isCacheable()) {
358                 methodCache.put(methodKey.createCopy(), method);
359             }
360         }
361         return method;
362     }
363 
364     public Constructor retrieveConstructor(Class[] arguments) {
365         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
366         if (constructor != null) {
367             return constructor;
368         }
369         else {
370             constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
371             if (constructor != null) {
372                 return constructor;
373             }
374         }
375         return null;
376     }
377 
378     public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
379         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
380         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
381         if (method == null) {
382             method = pickStaticMethod(methodName, arguments);
383             if (method != null) {
384                 staticMethodCache.put(methodKey.createCopy(), method);
385             }
386         }
387         return method;
388     }
389     /***
390      * Picks which method to invoke for the given object, method name and arguments
391      */
392     protected MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
393         MetaMethod method = null;
394         List methods = getMethods(methodName);
395         if (!methods.isEmpty()) {
396             Class[] argClasses = convertToTypeArray(arguments);
397             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
398             if (method == null) {
399                 int size = (arguments != null) ? arguments.length : 0;
400                 if (size == 1) {
401                     Object firstArgument = arguments[0];
402                     if (firstArgument instanceof List) {
403                         // lets coerce the list arguments into an array of
404                         // arguments
405                         // e.g. calling JFrame.setLocation( [100, 100] )
406 
407                         List list = (List) firstArgument;
408                         arguments = list.toArray();
409                         argClasses = convertToTypeArray(arguments);
410                         method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
411                         if (method==null) return null;
412                             return new TransformMetaMethod(method) {
413                                 public Object invoke(Object object, Object[] arguments) throws Exception {
414                                     Object firstArgument = arguments[0];
415                                     List list = (List) firstArgument;
416                                     arguments = list.toArray();
417                                     return super.invoke(object, arguments);
418                                 }
419                             };
420                     }
421                 }
422             }
423         }
424         return method;
425     }
426 
427     /***
428      * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
429      * this method is used only by ClassGenerator for static binding
430      * @param methodName
431      * @param arguments
432      * @return
433      */
434     protected MetaMethod pickMethod(String methodName, Class[] arguments) {
435         MetaMethod method = null;
436         List methods = getMethods(methodName);
437         if (!methods.isEmpty()) {
438             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
439 // no coersion at classgen time.
440 //            if (method == null) {
441 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
442 //            }
443         }
444         return method;
445     }
446 
447     public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
448         //        System.out.println("Calling static method: " + methodName + " on args: " + InvokerHelper.toString(arguments));
449         //        Class type = arguments == null ? null : arguments.getClass();
450         //        System.out.println("Argument  type: " + type);
451         //        System.out.println("Type of first arg: " + arguments[0] + " type: " + arguments[0].getClass());
452 
453         // lets try use the cache to find the method
454         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
455         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
456         if (method == null) {
457             method = pickStaticMethod(object, methodName, arguments);
458             if (method != null) {
459                 staticMethodCache.put(methodKey.createCopy(), method);
460             }
461         }
462 
463         if (method != null) {
464             return doMethodInvoke(object, method, arguments);
465         }
466         /*
467         List methods = getStaticMethods(methodName);
468         
469         if (!methods.isEmpty()) {
470             MetaMethod method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
471             if (method != null) {
472                 return doMethodInvoke(theClass, method, arguments);
473             }
474         }
475         
476         if (theClass != Class.class) {
477             try {
478                 return registry.getMetaClass(Class.class).invokeMethod(object, methodName, arguments);
479             }
480             catch (GroovyRuntimeException e) {
481                 // throw our own exception
482             }
483         }
484         */
485         throw new MissingMethodException(methodName, theClass, arguments);
486     }
487 
488     protected MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
489         MetaMethod method = null;
490         List methods = getStaticMethods(methodName);
491 
492         if (!methods.isEmpty()) {
493             method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false);
494         }
495 
496         if (method == null && theClass != Class.class) {
497             MetaClass classMetaClass = registry.getMetaClass(Class.class);
498             method = classMetaClass.pickMethod(object, methodName, arguments);
499         }
500         return method;
501     }
502 
503     protected MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
504         MetaMethod method = null;
505         List methods = getStaticMethods(methodName);
506 
507         if (!methods.isEmpty()) {
508             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
509 // disabled to keep consistant with the original version of pickStatciMethod
510 //            if (method == null) {
511 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
512 //            }
513         }
514 
515         if (method == null && theClass != Class.class) {
516             MetaClass classMetaClass = registry.getMetaClass(Class.class);
517             method = classMetaClass.pickMethod(methodName, arguments);
518         }
519         return method;
520     }
521 
522     public Object invokeConstructor(Object[] arguments) {
523         Class[] argClasses = convertToTypeArray(arguments);
524         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
525         if (constructor != null) {
526             return doConstructorInvoke(constructor, arguments);
527         }
528         else {
529             constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
530             if (constructor != null) {
531                 return doConstructorInvoke(constructor, arguments);
532             }
533         }
534 
535         if (arguments.length == 1) {
536             Object firstArgument = arguments[0];
537             if (firstArgument instanceof Map) {
538                 constructor = (Constructor) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false);
539                 if (constructor != null) {
540                     Object bean = doConstructorInvoke(constructor, EMPTY_ARRAY);
541                     setProperties(bean, ((Map) firstArgument));
542                     return bean;
543                 }
544             }
545         }
546         throw new GroovyRuntimeException(
547                     "Could not find matching constructor for: "
548                         + theClass.getName()
549                         + "("+InvokerHelper.toTypeString(arguments)+")");
550     }
551 
552     /***
553      * Sets a number of bean properties from the given Map where the keys are
554      * the String names of properties and the values are the values of the
555      * properties to set
556      */
557     public void setProperties(Object bean, Map map) {
558         for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
559             Map.Entry entry = (Map.Entry) iter.next();
560             String key = entry.getKey().toString();
561             
562             // do we have this property?
563             if(propertyMap.get(key) == null)
564                 continue;
565             
566             Object value = entry.getValue();
567             try {
568                 setProperty(bean, key, value);
569             }
570             catch (GroovyRuntimeException e) {
571                 // lets ignore missing properties
572                 /*** todo should replace this code with a getMetaProperty(key) != null check
573                  i.e. don't try and set a non-existent property
574                  */
575             }
576         }
577     }
578 
579     /***
580      * @return the given property's value on the object
581      */
582     public Object getProperty(final Object object, final String property) {
583         // look for the property in our map
584         MetaProperty mp = (MetaProperty) propertyMap.get(property);
585         if(mp != null) {
586             try {
587                 //System.out.println("we found a metaproperty for " + theClass.getName() +
588                 //  "." + property);
589                 // delegate the get operation to the metaproperty
590                 return mp.getProperty(object);
591             }
592             catch(Exception e) {
593                 throw new GroovyRuntimeException("Cannot read property: " + property);
594             }
595         }
596 
597         if (genericGetMethod == null) {
598             // Make sure there isn't a generic method in the "use" cases
599             List possibleGenericMethods = getMethods("get");
600             if (possibleGenericMethods != null) {
601                 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
602                     MetaMethod mmethod = (MetaMethod) i.next();
603                     Class[] paramTypes = mmethod.getParameterTypes();
604                     if (paramTypes.length == 1 && paramTypes[0] == String.class) {
605                         Object[] arguments = {property};
606                         Object answer = doMethodInvoke(object, mmethod, arguments);
607                         return answer;
608                     }
609                 }
610             }
611         } 
612         else {
613             Object[] arguments = { property };
614             Object answer = doMethodInvoke(object, genericGetMethod, arguments);
615             // jes bug? a property retrieved via a generic get() can't have a null value?
616             if (answer != null) {
617                 return answer;
618             }
619         }
620 
621         if (!CompilerConfiguration.isJsrGroovy()) {
622             // is the property the name of a method - in which case return a
623             // closure
624             List methods = getMethods(property);
625             if (!methods.isEmpty()) {
626                 return new MethodClosure(object, property);
627             }
628         }
629 
630         // lets try invoke a static getter method
631         // this case is for protected fields. I wish there was a better way...
632         Exception lastException = null;
633         try {
634             MetaMethod method = findGetter(object, "get" + capitalize(property));
635             if (method != null) {
636                 return doMethodInvoke(object, method, EMPTY_ARRAY);
637             }
638         }
639         catch (GroovyRuntimeException e) {
640             lastException = e;
641         }
642 
643         /*** todo or are we an extensible groovy class? */
644         if (genericGetMethod != null) {
645             return null;
646         }
647         else {
648             /*** todo these special cases should be special MetaClasses maybe */
649             if (object instanceof Class) {
650                 // lets try a static field
651                 return getStaticProperty((Class) object, property);
652             }
653             if (object instanceof Collection) {
654                 return DefaultGroovyMethods.getAt((Collection) object, property);
655             }
656             if (object instanceof Object[]) {
657                 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
658             }
659             if (object instanceof Object) {
660                 Field field = null;
661                 try {
662                     // lets try a public field
663                     field = object.getClass().getDeclaredField(property);
664                     return field.get(object);
665                 } catch (IllegalAccessException iae) {
666                     lastException = new IllegalPropertyAccessException(field,object.getClass());
667                 } catch (Exception e1) {
668                     // fall through
669                 }
670             }
671             
672             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
673             if (addListenerMethod != null) {
674                 /* @todo one day we could try return the previously registered Closure listener for easy removal */
675                 return null;
676             }
677 
678             if (lastException == null)
679                 throw new MissingPropertyException(property, theClass);
680             else
681                 throw new MissingPropertyException(property, theClass, lastException);
682         }
683     }
684 
685     /***
686      * Get all the properties defined for this type
687      * @return a list of MetaProperty objects
688      */
689     public List getProperties() {
690         // simply return the values of the metaproperty map as a List
691         return new ArrayList(propertyMap.values());
692     }
693     
694     /***
695      * This will build up the property map (Map of MetaProperty objects, keyed on 
696      * property name).
697      */
698     protected void setupProperties(PropertyDescriptor[] propertyDescriptors) {
699         MetaProperty mp;
700         Method method;
701         MetaMethod getter = null;
702         MetaMethod setter = null;
703         Class klass;
704         
705         // first get the public fields and create MetaFieldProperty objects
706         klass = theClass;
707         while(klass != null) {
708             Field[] fields = klass.getDeclaredFields();
709             for(int i = 0; i < fields.length; i++) {
710                 // we're only interested in publics
711                 if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0)
712                     continue;
713                 
714                 // see if we already got this
715                 if(propertyMap.get(fields[i].getName()) != null)
716                     continue;
717                 
718                 //System.out.println("adding field " + fields[i].getName() + 
719                 //  " for class " + klass.getName());
720                 // stick it in there!
721                 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
722             }
723             
724             // now get the super class
725             klass = klass.getSuperclass();
726         }
727         
728 		// if this an Array, then add the special read-only "length" property
729 		if(theClass.isArray()) {
730 			propertyMap.put("length", arrayLengthProperty);
731 		}
732 		
733         // now iterate over the map of property descriptors and generate 
734         // MetaBeanProperty objects
735         for(int i=0; i<propertyDescriptors.length; i++) {
736             PropertyDescriptor pd = propertyDescriptors[i];
737             // skip if the field already exists in the map
738             if(propertyMap.get(pd.getName()) != null)
739                 continue;
740             
741             // skip if the property type is unknown (this seems to be the case if the 
742             // property descriptor is based on a setX() method that has two parameters,
743             // which is not a valid property)
744             if(pd.getPropertyType() == null)
745                 continue;
746             
747             // get the getter method
748             method = pd.getReadMethod();
749             if(method != null)
750                 getter = findMethod(method);
751             else
752                 getter = null;
753             
754             // get the setter method
755             method = pd.getWriteMethod();
756             if(method != null)
757                 setter = findMethod(method);
758             else
759                 setter = null;
760             
761             // now create the MetaProperty object
762             //System.out.println("creating a bean property for class " +
763             //  theClass.getName() + ": " + pd.getName());
764                 
765             mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
766             
767             // put it in the list
768             propertyMap.put(pd.getName(), mp);
769         }
770         
771         // now look for any stray getters that may be used to define a property
772         klass = theClass;
773         while(klass != null) {
774             Method[] methods = klass.getDeclaredMethods();
775             for (int i = 0; i < methods.length; i++) {
776                 // filter out the privates
777                 if(Modifier.isPublic(methods[i].getModifiers()) == false)
778                     continue;
779                 
780                 method = methods[i];
781                 
782                 String methodName = method.getName();
783                 
784                 // is this a getter?
785                 if(methodName.startsWith("get") && 
786                     methodName.length() > 3 &&
787                     method.getParameterTypes().length == 0) {
788                     
789                     // get the name of the property
790                     String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
791                     
792                     // is this property already accounted for?
793                     mp = (MetaProperty) propertyMap.get(propName);
794                     if(mp != null) {
795                         // we may have already found the setter for this
796                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
797                             // update the getter method to this one
798                             ((MetaBeanProperty) mp).setGetter(findMethod(method));
799                         }
800                     }
801                     else {
802                         // we need to create a new property object
803                         // type of the property is what the get method returns
804                         MetaBeanProperty mbp = new MetaBeanProperty(propName, 
805                             method.getReturnType(),
806                             findMethod(method), null);
807                             
808                         // add it to the map
809                         propertyMap.put(propName, mbp);
810                     }
811                 }
812                 else if(methodName.startsWith("set") && 
813                     methodName.length() > 3 &&
814                     method.getParameterTypes().length == 1) {
815                     
816                     // get the name of the property
817                     String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
818                     
819                     // did we already find the getter of this?
820                     mp = (MetaProperty) propertyMap.get(propName);
821                     if(mp != null) {
822                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
823                             // update the setter method to this one
824                             ((MetaBeanProperty) mp).setSetter(findMethod(method));
825                         }
826                     }
827                     else {
828                         // this is a new property to add
829                         MetaBeanProperty mbp = new MetaBeanProperty(propName, 
830                                                                     method.getParameterTypes()[0],
831                                                                     null,
832                                                                     findMethod(method));
833                             
834                         // add it to the map
835                         propertyMap.put(propName, mbp);
836                     }
837                 }
838             }
839             
840             // now get the super class
841             klass = klass.getSuperclass();
842         }
843     }
844     
845     /***
846      * Sets the property value on an object
847      */
848     public void setProperty(Object object, String property, Object newValue) {
849         MetaProperty mp = (MetaProperty) propertyMap.get(property);
850         if(mp != null) {
851             try {
852                 mp.setProperty(object, newValue);
853                 return;
854             }
855             catch(ReadOnlyPropertyException e) {
856                 // just rethrow it; there's nothing left to do here
857                 throw e;
858             }
859             catch (TypeMismatchException e) {
860                 // tried to access to mismatched object.
861                 throw e;
862             }
863             catch (Exception e) {
864                 // if the value is a List see if we can construct the value
865                 // from a constructor
866                 if (newValue == null)
867                     return;
868                 if (newValue instanceof List) {
869                     List list = (List) newValue;
870                     int params = list.size();
871                     Constructor[] constructors = mp.getType().getConstructors();
872                     for (int i = 0; i < constructors.length; i++) {
873                         Constructor constructor = constructors[i];
874                         if (constructor.getParameterTypes().length == params) {
875                             Object value = doConstructorInvoke(constructor, list.toArray());
876                             mp.setProperty(object, value);
877                             return;
878                         }
879                     }
880                     
881                     // if value is an array  
882                     Class parameterType = mp.getType();
883                     if (parameterType.isArray()) {
884                         Object objArray = asPrimitiveArray(list, parameterType);
885                         mp.setProperty(object, objArray);
886                         return;
887                     }
888                 }
889 
890                 // if value is an multidimensional array  
891                 // jes currently this logic only supports metabeansproperties and
892                 // not metafieldproperties. It shouldn't be too hard to support
893                 // the latter...
894                 if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
895                     MetaBeanProperty mbp = (MetaBeanProperty) mp;
896                     List list = Arrays.asList((Object[])newValue);
897                     MetaMethod setter = mbp.getSetter();
898                     
899                     Class parameterType = setter.getParameterTypes()[0];
900                     Class arrayType = parameterType.getComponentType();
901                     Object objArray = Array.newInstance(arrayType, list.size());
902                     
903                     for (int i = 0; i < list.size(); i++) {
904                         List list2 =Arrays.asList((Object[]) list.get(i));
905                         Object objArray2 = asPrimitiveArray(list2, arrayType);
906                         Array.set(objArray, i, objArray2);
907                     }
908 
909                     doMethodInvoke(object, setter, new Object[]{
910                         objArray
911                     });
912                     return;
913                 }
914                 
915                 throw new MissingPropertyException(property, theClass, e);
916             }
917         }
918         
919         try {
920             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
921             if (addListenerMethod != null && newValue instanceof Closure) {
922                 // lets create a dynamic proxy
923                 Object proxy =
924                     createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
925                 doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
926                 return;
927             }
928 
929             if (genericSetMethod == null) {
930                 // Make sure there isn't a generic method in the "use" cases
931                 List possibleGenericMethods = getMethods("set");
932                 if (possibleGenericMethods != null) {
933                     for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
934                         MetaMethod mmethod = (MetaMethod) i.next();
935                         Class[] paramTypes = mmethod.getParameterTypes();
936                         if (paramTypes.length == 2 && paramTypes[0] == String.class) {
937                             Object[] arguments = {property, newValue};
938                             Object answer = doMethodInvoke(object, mmethod, arguments);
939                             return;
940                         }
941                     }
942                 }
943             } 
944             else {
945                 Object[] arguments = { property, newValue };
946                 doMethodInvoke(object, genericSetMethod, arguments);
947                 return;
948             }
949 
950             /*** todo or are we an extensible class? */
951 
952             // lets try invoke the set method
953             // this is kind of ugly: if it is a protected field, we fall
954             // all the way down to this klunky code. Need a better
955             // way to handle this situation...
956 
957             String method = "set" + capitalize(property);
958             try {
959                 invokeMethod(object, method, new Object[] { newValue });
960             }
961             catch (MissingMethodException e1) {
962                 Field field = null;
963                 try {
964                     field = object.getClass().getDeclaredField(property);
965                     //field.setAccessible(true);
966                     field.set(object, newValue);
967                 } catch (IllegalAccessException iae) {
968                     throw new IllegalPropertyAccessException(field,object.getClass());
969                 } catch (Exception e2) {
970                     throw new MissingPropertyException(property, theClass, e2);
971                 }
972             }
973             
974         }
975         catch (GroovyRuntimeException e) {
976             throw new MissingPropertyException(property, theClass, e);
977         }
978         
979     }
980 
981 
982     /***
983      * Looks up the given attribute (field) on the given object
984      */
985     public Object getAttribute(Object object, String attribute) {
986         try {
987             Field field = theClass.getDeclaredField(attribute);
988             field.setAccessible(true);
989             return field.get(object);
990         }
991         catch (NoSuchFieldException e) {
992             throw new MissingFieldException(attribute, theClass);
993         }
994         catch (IllegalAccessException e) {
995             throw new MissingFieldException(attribute, theClass, e);
996         }
997     }
998 
999     /***
1000      * Sets the given attribute (field) on the given object
1001      */
1002     public void setAttribute(Object object, String attribute, Object newValue) {
1003         try {
1004             Field field = theClass.getDeclaredField(attribute);
1005             field.setAccessible(true);
1006             field.set(object, newValue);
1007         }
1008         catch (NoSuchFieldException e) {
1009             throw new MissingFieldException(attribute, theClass);
1010         }
1011         catch (IllegalAccessException e) {
1012             throw new MissingFieldException(attribute, theClass, e);
1013         }
1014     }
1015 
1016     /***
1017      * Returns a callable object for the given method name on the object.
1018      * The object acts like a Closure in that it can be called, like a closure
1019      * and passed around - though really its a method pointer, not a closure per se.
1020      */
1021     public Closure getMethodPointer(Object object, String methodName) {
1022         return new MethodClosure(object, methodName);
1023     }
1024 
1025     /***
1026      * @param list
1027      * @param parameterType
1028      * @return
1029      */
1030     private Object asPrimitiveArray(List list, Class parameterType) {
1031         Class arrayType = parameterType.getComponentType();
1032         Object objArray = Array.newInstance(arrayType, list.size());
1033         for (int i = 0; i < list.size(); i++) {
1034             Object obj = list.get(i);
1035             if (arrayType.isPrimitive()) {
1036                 if (obj instanceof Integer) {
1037                     Array.setInt(objArray, i, ((Integer) obj).intValue());
1038                 }
1039                 else if (obj instanceof Double) {
1040                     Array.setDouble(objArray, i, ((Double) obj).doubleValue());
1041                 }
1042                 else if (obj instanceof Boolean) {
1043                     Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
1044                 }
1045                 else if (obj instanceof Long) {
1046                     Array.setLong(objArray, i, ((Long) obj).longValue());
1047                 }
1048                 else if (obj instanceof Float) {
1049                     Array.setFloat(objArray, i, ((Float) obj).floatValue());
1050                 }
1051                 else if (obj instanceof Character) {
1052                     Array.setChar(objArray, i, ((Character) obj).charValue());
1053                 }
1054                 else if (obj instanceof Byte) {
1055                     Array.setByte(objArray, i, ((Byte) obj).byteValue());
1056                 }
1057                 else if (obj instanceof Short) {
1058                     Array.setShort(objArray, i, ((Short) obj).shortValue());
1059                 }
1060             }
1061             else {
1062                 Array.set(objArray, i, obj);
1063             }
1064         }
1065         return objArray;
1066     }
1067     
1068     public ClassNode getClassNode() {
1069         if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
1070             // lets try load it from the classpath
1071             String className = theClass.getName();
1072             String groovyFile = className;
1073             int idx = groovyFile.indexOf('$');
1074             if (idx > 0) {
1075                 groovyFile = groovyFile.substring(0, idx);
1076             }
1077             groovyFile = groovyFile.replace('.', '/') + ".groovy";
1078 
1079             //System.out.println("Attempting to load: " + groovyFile);
1080             URL url = theClass.getClassLoader().getResource(groovyFile);
1081             if (url == null) {
1082                 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
1083             }
1084             if (url != null) {
1085                 try {
1086 
1087                     /***
1088                      * todo there is no CompileUnit in scope so class name
1089                      * checking won't work but that mostly affects the bytecode
1090                      * generation rather than viewing the AST
1091                      */
1092 
1093                     CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1094                         public void call( ClassVisitor writer, ClassNode node ) {
1095                             if( node.getName().equals(theClass.getName()) ) {
1096                                 MetaClass.this.classNode = node;
1097                             }
1098                         }
1099                     };
1100                     
1101                     
1102                     CompilationUnit unit = new CompilationUnit( getClass().getClassLoader() );
1103                     unit.setClassgenCallback( search );
1104                     unit.addSource( url );
1105                     unit.compile( Phases.CLASS_GENERATION );
1106                 }
1107                 catch (Exception e) {
1108                     throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
1109                 }
1110             }
1111 
1112         }
1113         return classNode;
1114     }
1115 
1116     public String toString() {
1117         return super.toString() + "[" + theClass + "]";
1118     }
1119 
1120     // Implementation methods
1121     //-------------------------------------------------------------------------
1122 
1123     /***
1124      * Converts the given object into an array; if its an array then just cast
1125      * otherwise wrap it in an array
1126      */
1127     protected Object[] asArray(Object arguments) {
1128         if (arguments == null) {
1129             return EMPTY_ARRAY;
1130         }
1131         if (arguments instanceof Tuple) {
1132             Tuple tuple = (Tuple) arguments;
1133             return tuple.toArray();
1134         }
1135         if (arguments instanceof Object[]) {
1136             return (Object[]) arguments;
1137         }
1138         else {
1139             return new Object[] { arguments };
1140         }
1141     }
1142 	
1143     /***
1144      * @param listenerType
1145      *            the interface of the listener to proxy
1146      * @param listenerMethodName
1147      *            the name of the method in the listener API to call the
1148      *            closure on
1149      * @param closure
1150      *            the closure to invoke on the listenerMethodName method
1151      *            invocation
1152      * @return a dynamic proxy which calls the given closure on the given
1153      *         method name
1154      */
1155     protected Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
1156         InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
1157         return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
1158     }
1159 
1160     /***
1161      * Adds all the methods declared in the given class to the metaclass
1162      * ignoring any matching methods already defined by a derived class
1163      * 
1164      * @param theClass
1165      */
1166     protected void addMethods(Class theClass, boolean forceOverwrite) {
1167         // add methods directly declared in the class
1168         Method[] methodArray = theClass.getDeclaredMethods();
1169         for (int i = 0; i < methodArray.length; i++) {
1170             Method reflectionMethod = methodArray[i];
1171             if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
1172                 continue;
1173             }
1174             MetaMethod method = createMetaMethod(reflectionMethod);
1175             addMethod(method,forceOverwrite);
1176         }
1177     }
1178 
1179     protected void addMethod(MetaMethod method, boolean forceOverwrite) {
1180         String name = method.getName();
1181 
1182         //System.out.println(theClass.getName() + " == " + name + Arrays.asList(method.getParameterTypes()));
1183 
1184         if (isGenericGetMethod(method) && genericGetMethod == null) {
1185             genericGetMethod = method;
1186         }
1187         else if (isGenericSetMethod(method) && genericSetMethod == null) {
1188             genericSetMethod = method;
1189         }
1190         if (method.isStatic()) {
1191             List list = (List) staticMethodIndex.get(name);
1192             if (list == null) {
1193                 list = new ArrayList();
1194                 staticMethodIndex.put(name, list);
1195                 list.add(method);
1196             }
1197             else {
1198                 if (!containsMatchingMethod(list, method)) {
1199                     list.add(method);
1200                 }
1201             }
1202         }
1203 
1204         List list = (List) methodIndex.get(name);
1205         if (list == null) {
1206             list = new ArrayList();
1207             methodIndex.put(name, list);
1208             list.add(method);
1209         }
1210         else {
1211             if (forceOverwrite) {
1212                 removeMatchingMethod(list,method);
1213                 list.add(method);
1214             } else if (!containsMatchingMethod(list, method)) {
1215                 list.add(method);
1216             }
1217         }
1218     }
1219 
1220     /***
1221      * @return true if a method of the same matching prototype was found in the
1222      *         list
1223      */
1224     protected boolean containsMatchingMethod(List list, MetaMethod method) {
1225         for (Iterator iter = list.iterator(); iter.hasNext();) {
1226             MetaMethod aMethod = (MetaMethod) iter.next();
1227             Class[] params1 = aMethod.getParameterTypes();
1228             Class[] params2 = method.getParameterTypes();
1229             if (params1.length == params2.length) {
1230                 boolean matches = true;
1231                 for (int i = 0; i < params1.length; i++) {
1232                     if (params1[i] != params2[i]) {
1233                         matches = false;
1234                         break;
1235                     }
1236                 }
1237                 if (matches) {
1238                     return true;
1239                 }
1240             }
1241         }
1242         return false;
1243     }
1244     
1245     /***
1246      * remove a method of the same matching prototype was found in the list
1247      */
1248     protected void removeMatchingMethod(List list, MetaMethod method) {
1249         for (Iterator iter = list.iterator(); iter.hasNext();) {
1250             MetaMethod aMethod = (MetaMethod) iter.next();
1251             Class[] params1 = aMethod.getParameterTypes();
1252             Class[] params2 = method.getParameterTypes();
1253             if (params1.length == params2.length) {
1254                 boolean matches = true;
1255                 for (int i = 0; i < params1.length; i++) {
1256                     if (params1[i] != params2[i]) {
1257                         matches = false;
1258                         break;
1259                     }
1260                 }
1261                 if (matches) {
1262                     iter.remove();
1263                     return;
1264                 }
1265             }
1266         }
1267         return;
1268     }
1269     
1270 
1271     /***
1272      * Adds all of the newly defined methods from the given class to this
1273      * metaclass
1274      * 
1275      * @param theClass
1276      */
1277     protected void addNewStaticMethodsFrom(Class theClass) {
1278         MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
1279         Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
1280         while (iter.hasNext()) {
1281             MetaMethod method = (MetaMethod) iter.next();
1282             addMethod(method,false);
1283             newGroovyMethodsList.add(method);
1284         }
1285     }
1286 
1287     /***
1288      * @return the value of the static property of the given class
1289      */
1290     protected Object getStaticProperty(Class aClass, String property) {
1291         //System.out.println("Invoking property: " + property + " on class: "
1292         // + aClass);
1293 
1294         Exception lastException = null;
1295         try {
1296             Field field = aClass.getField(property);
1297             if (field != null) {
1298                 if ((field.getModifiers() & Modifier.STATIC) != 0) {
1299                     return field.get(null);
1300                 }
1301             }
1302         }
1303         catch (Exception e) {
1304             lastException = e;
1305         }
1306 
1307         // lets try invoke a static getter method
1308         try {
1309             MetaMethod method = findStaticGetter(aClass, "get" + capitalize(property));
1310             if (method != null) {
1311                 return doMethodInvoke(aClass, method, EMPTY_ARRAY);
1312             }
1313         }
1314         catch (GroovyRuntimeException e) {
1315             throw new MissingPropertyException(property, aClass, e);
1316         }
1317 
1318         if (lastException == null) {
1319             throw new MissingPropertyException(property, aClass);
1320         }
1321         else {
1322             throw new MissingPropertyException(property, aClass, lastException);
1323         }
1324     }
1325 
1326     /***
1327      * @return the matching method which should be found
1328      */
1329     protected MetaMethod findMethod(Method aMethod) {
1330         List methods = getMethods(aMethod.getName());
1331         for (Iterator iter = methods.iterator(); iter.hasNext();) {
1332             MetaMethod method = (MetaMethod) iter.next();
1333             if (method.isMethod(aMethod)) {
1334                 return method;
1335             }
1336         }
1337         //log.warning("Creating reflection based dispatcher for: " + aMethod);
1338         return new ReflectionMetaMethod(aMethod);
1339     }
1340 
1341     /***
1342      * @return the getter method for the given object
1343      */
1344     protected MetaMethod findGetter(Object object, String name) {
1345         List methods = getMethods(name);
1346         for (Iterator iter = methods.iterator(); iter.hasNext();) {
1347             MetaMethod method = (MetaMethod) iter.next();
1348             if (method.getParameterTypes().length == 0) {
1349                 return method;
1350             }
1351         }
1352         return null;
1353     }
1354 
1355     /***
1356      * @return the Method of the given name with no parameters or null
1357      */
1358     protected MetaMethod findStaticGetter(Class type, String name) {
1359         List methods = getStaticMethods(name);
1360         for (Iterator iter = methods.iterator(); iter.hasNext();) {
1361             MetaMethod method = (MetaMethod) iter.next();
1362             if (method.getParameterTypes().length == 0) {
1363                 return method;
1364             }
1365         }
1366 
1367         /*** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1368         try {
1369             Method method = type.getMethod(name, EMPTY_TYPE_ARRAY);
1370             if ((method.getModifiers() & Modifier.STATIC) != 0) {
1371                 return findMethod(method);
1372             }
1373             else {
1374                 return null;
1375             }
1376         }
1377         catch (Exception e) {
1378             return null;
1379         }
1380     }
1381 
1382     protected Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
1383         //System.out.println("Evaluating method: " + method);
1384         //System.out.println("on object: " + object + " with arguments: " +
1385         // InvokerHelper.toString(argumentArray));
1386         //System.out.println(this.theClass);
1387 
1388         try {
1389             if (argumentArray == null) {
1390                 argumentArray = EMPTY_ARRAY;
1391             }
1392             else if (method.getParameterTypes().length == 1 && argumentArray.length == 0) {
1393                 argumentArray = ARRAY_WITH_NULL;
1394             }
1395             return method.invoke(object, argumentArray);
1396         }
1397         catch (ClassCastException e) {
1398             if (coerceGStrings(argumentArray)) {
1399                 try {
1400                     return doMethodInvoke(object, method, argumentArray);
1401                 }
1402                 catch (Exception e2) {
1403                     // allow fall through
1404                 }
1405             }
1406             throw new GroovyRuntimeException(
1407                 "failed to invoke method: "
1408                     + method
1409                     + " on: "
1410                     + object
1411                     + " with arguments: "
1412                     + InvokerHelper.toString(argumentArray)
1413                     + " reason: "
1414                     + e,
1415                 e);
1416         }
1417         catch (InvocationTargetException e) {
1418             /*Throwable t = e.getTargetException();
1419             if (t instanceof Error) {
1420                 Error error = (Error) t;
1421                 throw error;
1422             }
1423             if (t instanceof RuntimeException) {
1424                 RuntimeException runtimeEx = (RuntimeException) t;
1425                 throw runtimeEx;
1426             }*/
1427             throw new InvokerInvocationException(e);
1428         }
1429         catch (IllegalAccessException e) {
1430             throw new GroovyRuntimeException(
1431                 "could not access method: "
1432                     + method
1433                     + " on: "
1434                     + object
1435                     + " with arguments: "
1436                     + InvokerHelper.toString(argumentArray)
1437                     + " reason: "
1438                     + e,
1439                 e);
1440         }
1441         catch (IllegalArgumentException e) {
1442             if (coerceGStrings(argumentArray)) {
1443                 try {
1444                     return doMethodInvoke(object, method, argumentArray);
1445                 }
1446                 catch (Exception e2) {
1447                     // allow fall through
1448                 }
1449             }
1450             Object[] args = coerceNumbers(method, argumentArray);
1451             if (args != null && !Arrays.equals(argumentArray,args)) {
1452                 try {
1453                     return doMethodInvoke(object, method, args);
1454                 }
1455                 catch (Exception e3) {
1456                     // allow fall through
1457                 }
1458             }
1459             throw new GroovyRuntimeException(
1460                     "failed to invoke method: "
1461                     + method
1462                     + " on: "
1463                     + object
1464                     + " with arguments: "
1465                     + InvokerHelper.toString(argumentArray)
1466                     + "reason: "
1467                     + e
1468             );
1469         }
1470         catch (RuntimeException e) {
1471             throw e;
1472         }
1473         catch (Exception e) {
1474             throw new GroovyRuntimeException(
1475                 "failed to invoke method: "
1476                     + method
1477                     + " on: "
1478                     + object
1479                     + " with arguments: "
1480                     + InvokerHelper.toString(argumentArray)
1481                     + " reason: "
1482                     + e,
1483                 e);
1484         }
1485     }
1486 
1487     private static Object[] coerceNumbers(MetaMethod method, Object[] arguments) {
1488         Object[] ans = null;
1489         boolean coerced = false; // to indicate that at least one param is coerced
1490 
1491         Class[] params = method.getParameterTypes();
1492 
1493         if (params.length != arguments.length) {
1494             return null;
1495         }
1496 
1497         ans = new Object[arguments.length];
1498 
1499         for (int i = 0, size = arguments.length; i < size; i++) {
1500             Object argument = arguments[i];
1501             Class param = params[i];
1502             if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) { // Number types
1503                 if (param == Byte.class || param == Byte.TYPE ) {
1504                     ans[i] = new Byte(((Number)argument).byteValue());
1505                     coerced = true; continue;
1506                 }
1507                 if (param == Double.class || param == Double.TYPE) {
1508                     ans[i] = new Double(((Number)argument).doubleValue());
1509                     coerced = true; continue;
1510                 }
1511                 if (param == Float.class || param == Float.TYPE) {
1512                     ans[i] = new Float(((Number)argument).floatValue());
1513                     coerced = true; continue;
1514                 }
1515                 if (param == Integer.class || param == Integer.TYPE) {
1516                     ans[i] = new Integer(((Number)argument).intValue());
1517                     coerced = true; continue;
1518                 }
1519                 if (param == Long.class || param == Long.TYPE) {
1520                     ans[i] = new Long(((Number)argument).longValue());
1521                     coerced = true; continue;
1522                 }
1523                 if (param == Short.class || param == Short.TYPE) {
1524                     ans[i] = new Short(((Number)argument).shortValue());
1525                     coerced = true; continue;
1526                 }
1527                 if (param == BigDecimal.class ) {
1528                     ans[i] = new BigDecimal(((Number)argument).doubleValue());
1529                     coerced = true; continue;
1530                 }
1531                 if (param == BigInteger.class) {
1532                     ans[i] = new BigInteger(String.valueOf(((Number)argument).longValue()));
1533                     coerced = true; continue;
1534                 }
1535             }
1536             else if (param.isArray() && argument.getClass().isArray()) {
1537                 Class paramElem = param.getComponentType();
1538                 if (paramElem.isPrimitive()) {
1539                     if (paramElem == boolean.class && argument.getClass().getName().equals("[Ljava.lang.Boolean;")) {
1540                         ans[i] = InvokerHelper.convertToBooleanArray(argument);
1541                         coerced = true;
1542                         continue;
1543                     }
1544                     if (paramElem == byte.class && argument.getClass().getName().equals("[Ljava.lang.Byte;")) {
1545                         ans[i] = InvokerHelper.convertToByteArray(argument);
1546                         coerced = true;
1547                         continue;
1548                     }
1549                     if (paramElem == char.class && argument.getClass().getName().equals("[Ljava.lang.Character;")) {
1550                         ans[i] = InvokerHelper.convertToCharArray(argument);
1551                         coerced = true;
1552                         continue;
1553                     }
1554                     if (paramElem == short.class && argument.getClass().getName().equals("[Ljava.lang.Short;")) {
1555                         ans[i] = InvokerHelper.convertToShortArray(argument);
1556                         coerced = true;
1557                         continue;
1558                     }
1559                     if (paramElem == int.class && argument.getClass().getName().equals("[Ljava.lang.Integer;")) {
1560                         ans[i] = InvokerHelper.convertToIntArray(argument);
1561                         coerced = true;
1562                         continue;
1563                     }
1564                     if (paramElem == long.class
1565                             && argument.getClass().getName().equals("[Ljava.lang.Long;")
1566                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1567                                                             ) {
1568                         ans[i] = InvokerHelper.convertToLongArray(argument);
1569                         coerced = true;
1570                         continue;
1571                     }
1572                     if (paramElem == float.class
1573                             && argument.getClass().getName().equals("[Ljava.lang.Float;")
1574                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1575                                                             ) {
1576                         ans[i] = InvokerHelper.convertToFloatArray(argument);
1577                         coerced = true;
1578                         continue;
1579                     }
1580                     if (paramElem == double.class &&
1581                             argument.getClass().getName().equals("[Ljava.lang.Double;") &&
1582                             argument.getClass().getName().equals("[Ljava.lang.BigDecimal;") &&
1583                             argument.getClass().getName().equals("[Ljava.lang.Float;")) {
1584                         ans[i] = InvokerHelper.convertToDoubleArray(argument);
1585                         coerced = true;
1586                         continue;
1587                     }
1588                 }
1589             }
1590         }
1591         return coerced ? ans : null;
1592     }
1593 
1594     protected Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
1595         //System.out.println("Evaluating constructor: " + constructor + " with
1596         // arguments: " + InvokerHelper.toString(argumentArray));
1597         //System.out.println(this.theClass);
1598 
1599         try {
1600 			// the following patch was provided by Mori Kouhei to fix JIRA 435
1601 			/* but it opens the ctor up to everyone, so it is no longer private!
1602             final Constructor ctor = constructor;
1603             AccessController.doPrivileged(new PrivilegedAction() {
1604                 public Object run() {
1605                     ctor.setAccessible(ctor.getDeclaringClass().equals(theClass));
1606                     return null;
1607                 }
1608             });
1609 			*/
1610 			// end of patch
1611 
1612             return constructor.newInstance(argumentArray);
1613         }
1614         catch (InvocationTargetException e) {
1615             /*Throwable t = e.getTargetException();
1616             if (t instanceof Error) {
1617                 Error error = (Error) t;
1618                 throw error;
1619             }
1620             if (t instanceof RuntimeException) {
1621                 RuntimeException runtimeEx = (RuntimeException) t;
1622                 throw runtimeEx;
1623             }*/
1624             throw new InvokerInvocationException(e);
1625         }
1626         catch (IllegalArgumentException e) {
1627             if (coerceGStrings(argumentArray)) {
1628                 try {
1629                     return constructor.newInstance(argumentArray);
1630                 }
1631                 catch (Exception e2) {
1632                     // allow fall through
1633                 }
1634             }
1635             throw new GroovyRuntimeException(
1636                 "failed to invoke constructor: "
1637                     + constructor
1638                     + " with arguments: "
1639                     + InvokerHelper.toString(argumentArray)
1640                     + " reason: "
1641                     + e);
1642         }
1643         catch (IllegalAccessException e) {
1644             throw new GroovyRuntimeException(
1645                 "could not access constructor: "
1646                     + constructor
1647                     + " with arguments: "
1648                     + InvokerHelper.toString(argumentArray)
1649                     + " reason: "
1650                     + e);
1651         }
1652         catch (Exception e) {
1653             throw new GroovyRuntimeException(
1654                 "failed to invoke constructor: "
1655                     + constructor
1656                     + " with arguments: "
1657                     + InvokerHelper.toString(argumentArray)
1658                     + " reason: "
1659                     + e,
1660                     e);
1661         }
1662     }
1663 
1664     /***
1665      * Chooses the correct method to use from a list of methods which match by
1666      * name.
1667      * 
1668      * @param methods
1669      *            the possible methods to choose from
1670      * @param arguments
1671      *            the original argument to the method
1672      * @return
1673      */
1674     protected Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
1675         int methodCount = methods.size();
1676         if (methodCount <= 0) {
1677             return null;
1678         }
1679         else if (methodCount == 1) {
1680             Object method = methods.get(0);
1681             if (isValidMethod(method, arguments, coerce)) {
1682                 return method;
1683             }
1684             return null;
1685         }
1686         Object answer = null;
1687         if (arguments == null || arguments.length == 0) {
1688             answer = chooseEmptyMethodParams(methods);
1689         }
1690         else if (arguments.length == 1 && arguments[0] == null) {
1691             answer = chooseMostGeneralMethodWith1NullParam(methods);
1692         }
1693         else {
1694             List matchingMethods = new ArrayList();
1695 
1696             for (Iterator iter = methods.iterator(); iter.hasNext();) {
1697                 Object method = iter.next();
1698                 Class[] paramTypes;
1699 
1700                 // making this false helps find matches
1701                 if (isValidMethod(method, arguments, coerce)) {
1702                     matchingMethods.add(method);
1703                 }
1704             }
1705             if (matchingMethods.isEmpty()) {
1706                 return null;
1707             }
1708             else if (matchingMethods.size() == 1) {
1709                 return matchingMethods.get(0);
1710             }
1711             return chooseMostSpecificParams(methodName, matchingMethods, arguments);
1712 
1713         }
1714         if (answer != null) {
1715             return answer;
1716         }
1717         throw new GroovyRuntimeException(
1718             "Could not find which method to invoke from this list: "
1719                 + methods
1720                 + " for arguments: "
1721                 + InvokerHelper.toString(arguments));
1722     }
1723 
1724     protected boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
1725         Class[] paramTypes = getParameterTypes(method);
1726         return isValidMethod(paramTypes, arguments, includeCoerce);
1727     }
1728 
1729     public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
1730         if (arguments == null) {
1731             return true;
1732         }
1733         int size = arguments.length;
1734         boolean validMethod = false;
1735         if (paramTypes.length == size) {
1736             // lets check the parameter types match
1737             validMethod = true;
1738             for (int i = 0; i < size; i++) {
1739                 if (!isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) {
1740                     validMethod = false;
1741                 }
1742             }
1743         }
1744         else {
1745             if (paramTypes.length == 1 && size == 0) {
1746                 return true;
1747             }
1748         }
1749         return validMethod;
1750     }
1751 
1752     private boolean isSuperclass(Class claszz, Class superclass) {
1753         while (claszz!=null) {
1754             if (claszz==superclass) return true;
1755             claszz = claszz.getSuperclass();
1756         }
1757         return false;
1758     }
1759     
1760     private Class[] wrap(Class[] classes) {
1761         Class[] wrappedArguments = new Class[classes.length];
1762         for (int i = 0; i < wrappedArguments.length; i++) {
1763             Class c = classes[i];
1764             if (c==null) continue;
1765             if (c.isPrimitive()) {
1766                 if (c==Integer.TYPE) {
1767                     c=Integer.class;
1768                 } else if (c==Byte.TYPE) {
1769                     c=Byte.class;
1770                 } else if (c==Long.TYPE) {
1771                     c=Long.class;
1772                 } else if (c==Double.TYPE) {
1773                     c=Double.class;
1774                 } else if (c==Float.TYPE) {
1775                     c=Float.class;
1776                 }
1777             } else if (isSuperclass(c,GString.class)) {
1778                 c = String.class;
1779             }
1780             wrappedArguments[i]=c;
1781         } 
1782         return wrappedArguments;
1783     }
1784     
1785     protected Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
1786         
1787         Class[] wrappedArguments = wrap(arguments);
1788         LinkedList directMatches = new LinkedList();
1789         // test for a method with equal classes (natives are wrapped
1790         for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1791             Object method = iter.next();
1792             Class[] paramTypes = wrap(getParameterTypes(method));
1793             if (Arrays.equals(wrappedArguments, paramTypes)) directMatches.add(method);
1794         }
1795         if (directMatches.size()>0) {
1796             matchingMethods = directMatches;
1797         }
1798         if (directMatches.size()>1) {
1799             matchingMethods = directMatches;
1800             // we have more then one possible match for wrapped natives
1801             // so next test without using wrapping
1802             directMatches = new LinkedList();
1803             for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1804                 Object method = iter.next();
1805                 Class[] paramTypes = getParameterTypes(method);
1806                 if (Arrays.equals(arguments, paramTypes)) directMatches.add(method);
1807             }
1808             if (directMatches.size()==1) matchingMethods = directMatches;
1809         }
1810         
1811         // filter out cases where we don't have a superclass
1812         List superclassMatches = new ArrayList(matchingMethods);
1813         for (Iterator iter = superclassMatches.iterator(); iter.hasNext(); ) {
1814             Object method = iter.next();
1815             Class[] paramTypes = getParameterTypes(method);
1816             for (int i=0; i<paramTypes.length; i++) {
1817                 if (!isSuperclass(arguments[i],paramTypes[i])) {
1818                     iter.remove();
1819                     break; //return from the inner for
1820                 }
1821             }
1822         }
1823         if (superclassMatches.size()!=0) {
1824             //if not all methods are filtered out use the filtered methods
1825             matchingMethods = superclassMatches;
1826         }
1827         
1828         Object answer = null;
1829         int size = arguments.length;
1830         Class[] mostSpecificTypes = null;
1831         for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1832             Object method = iter.next();
1833             Class[] paramTypes = getParameterTypes(method);
1834             if (answer == null) {
1835                 answer = method;
1836                 mostSpecificTypes = paramTypes;
1837             }
1838             else {
1839                 boolean useThisMethod = false;
1840                 for (int i = 0; i < size; i++) {
1841                     Class mostSpecificType = mostSpecificTypes[i];
1842                     Class type = paramTypes[i];
1843 
1844                     if (!isAssignableFrom(mostSpecificType, type)) {
1845                         useThisMethod = true;
1846                         break;
1847                     }
1848                 }
1849                 if (useThisMethod) {
1850 
1851                     if (size > 1) {
1852                         checkForInvalidOverloading(name, mostSpecificTypes, paramTypes);
1853                     }
1854 
1855                     answer = method;
1856                     mostSpecificTypes = paramTypes;
1857                 }
1858             }
1859         }
1860         return answer;
1861     }
1862 
1863     /***
1864      * Checks that one of the parameter types is a superset of the other and
1865      * that the two lists of types don't conflict. e.g. foo(String, Object) and
1866      * foo(Object, String) would conflict if called with foo("a", "b").
1867      * 
1868      * Note that this method is only called with 2 possible signatures. i.e.
1869      * possible invalid combinations will already have been filtered out. So if
1870      * there were methods foo(String, Object) and foo(Object, String) then one
1871      * of these would be already filtered out if foo was called as foo(12, "a")
1872      */
1873     protected void checkForInvalidOverloading(String name, Class[] baseTypes, Class[] derivedTypes) {
1874         for (int i = 0, size = baseTypes.length; i < size; i++) {
1875             Class baseType = baseTypes[i];
1876             Class derivedType = derivedTypes[i];
1877             if (!isAssignableFrom(derivedType, baseType)) {
1878                 throw new GroovyRuntimeException(
1879                     "Ambiguous method overloading for method: "
1880                         + name
1881                         + ". Cannot resolve which method to invoke due to overlapping prototypes between: "
1882                         + InvokerHelper.toString(baseTypes)
1883                         + " and: "
1884                         + InvokerHelper.toString(derivedTypes));
1885             }
1886         }
1887     }
1888 
1889     protected Class[] getParameterTypes(Object methodOrConstructor) {
1890         if (methodOrConstructor instanceof MetaMethod) {
1891             MetaMethod method = (MetaMethod) methodOrConstructor;
1892             return method.getParameterTypes();
1893         }
1894         if (methodOrConstructor instanceof Method) {
1895             Method method = (Method) methodOrConstructor;
1896             return method.getParameterTypes();
1897         }
1898         if (methodOrConstructor instanceof Constructor) {
1899             Constructor constructor = (Constructor) methodOrConstructor;
1900             return constructor.getParameterTypes();
1901         }
1902         throw new IllegalArgumentException("Must be a Method or Constructor");
1903     }
1904 
1905     /***
1906      * @return the method with 1 parameter which takes the most general type of
1907      *         object (e.g. Object) ignoring primitve types
1908      */
1909     protected Object chooseMostGeneralMethodWith1NullParam(List methods) {
1910         // lets look for methods with 1 argument which matches the type of the
1911         // arguments
1912         Class closestClass = null;
1913         Object answer = null;
1914 
1915         for (Iterator iter = methods.iterator(); iter.hasNext();) {
1916             Object method = iter.next();
1917             Class[] paramTypes = getParameterTypes(method);
1918             int paramLength = paramTypes.length;
1919             if (paramLength == 1) {
1920                 Class theType = paramTypes[0];
1921 				if (theType.isPrimitive()) continue;
1922                 if (closestClass == null || isAssignableFrom(closestClass, theType)) {
1923                     closestClass = theType;
1924                     answer = method;
1925                 }                
1926             }
1927         }
1928         return answer;
1929     }
1930 
1931     /***
1932      * @return the method with 1 parameter which takes the most general type of
1933      *         object (e.g. Object)
1934      */
1935     protected Object chooseEmptyMethodParams(List methods) {
1936         for (Iterator iter = methods.iterator(); iter.hasNext();) {
1937             Object method = iter.next();
1938             Class[] paramTypes = getParameterTypes(method);
1939             int paramLength = paramTypes.length;
1940             if (paramLength == 0) {
1941                 return method;
1942             }
1943         }
1944         return null;
1945     }
1946 
1947     protected static boolean isCompatibleInstance(Class type, Object value, boolean includeCoerce) {
1948         boolean answer = value == null || type.isInstance(value);
1949         if (!answer) {
1950             if (type.isPrimitive()) {
1951                 if (type == int.class) {
1952                     return value instanceof Integer;
1953                 }
1954                 else if (type == double.class) {
1955                     return value instanceof Double || value instanceof Float || value instanceof Integer || value instanceof BigDecimal;
1956                 }
1957                 else if (type == boolean.class) {
1958                     return value instanceof Boolean;
1959                 }
1960                 else if (type == long.class) {
1961                     return value instanceof Long || value instanceof Integer;
1962                 }
1963                 else if (type == float.class) {
1964                     return value instanceof Float || value instanceof Integer;
1965                 }
1966                 else if (type == char.class) {
1967                     return value instanceof Character;
1968                 }
1969                 else if (type == byte.class) {
1970                     return value instanceof Byte;
1971                 }
1972                 else if (type == short.class) {
1973                     return value instanceof Short;
1974                 }
1975             }
1976             else if(type.isArray() && value.getClass().isArray()) {
1977                 return isCompatibleClass(type.getComponentType(), value.getClass().getComponentType(), false);
1978             }
1979             else if (includeCoerce) {
1980                 if (type == String.class && value instanceof GString) {
1981                     return true;
1982                 }
1983                 else if (value instanceof Number) {
1984                     // lets allow numbers to be coerced downwards?
1985                     return Number.class.isAssignableFrom(type);
1986                 }
1987             }
1988         }
1989         return answer;
1990     }
1991     protected static boolean isCompatibleClass(Class type, Class value, boolean includeCoerce) {
1992         boolean answer = value == null || type.isAssignableFrom(value); // this might have taken care of primitive types, rendering part of the following code unnecessary
1993         if (!answer) {
1994             if (type.isPrimitive()) {
1995                 if (type == int.class) {
1996                     return value == Integer.class;// || value == BigDecimal.class; //br added BigDecimal
1997                 }
1998                 else if (type == double.class) {
1999                     return value == Double.class || value == Float.class || value == Integer.class || value == BigDecimal.class;
2000                 }
2001                 else if (type == boolean.class) {
2002                     return value == Boolean.class;
2003                 }
2004                 else if (type == long.class) {
2005                     return value == Long.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
2006                 }
2007                 else if (type == float.class) {
2008                     return value == Float.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
2009                 }
2010                 else if (type == char.class) {
2011                     return value == Character.class;
2012                 }
2013                 else if (type == byte.class) {
2014                     return value == Byte.class;
2015                 }
2016                 else if (type == short.class) {
2017                     return value == Short.class;
2018                 }
2019             }
2020             else if(type.isArray() && value.isArray()) {
2021                 return isCompatibleClass(type.getComponentType(), value.getComponentType(), false);
2022             }
2023             else if (includeCoerce) {
2024 //if (type == String.class && value == GString.class) {
2025                 if (type == String.class && GString.class.isAssignableFrom(value)) {
2026                     return true;
2027                 }
2028                 else if (value == Number.class) {
2029                     // lets allow numbers to be coerced downwards?
2030                     return Number.class.isAssignableFrom(type);
2031                 }
2032             }
2033         }
2034         return answer;
2035     }
2036 
2037     protected boolean isAssignableFrom(Class mostSpecificType, Class type) {
2038         // let's handle primitives
2039         if (mostSpecificType.isPrimitive() && type.isPrimitive()) {
2040             if (mostSpecificType == type) {
2041                 return true;
2042             }
2043             else {  // note: there is not coercion for boolean and char. Range matters, precision doesn't
2044                 if (type == int.class) {
2045                     return
2046                             mostSpecificType == int.class
2047                             || mostSpecificType == short.class
2048                             || mostSpecificType == byte.class;
2049                 }
2050                 else if (type == double.class) {
2051                     return
2052                             mostSpecificType == double.class
2053                             || mostSpecificType == int.class
2054                             || mostSpecificType == long.class
2055                             || mostSpecificType == short.class
2056                             || mostSpecificType == byte.class
2057                             || mostSpecificType == float.class;
2058                 }
2059                 else if (type == long.class) {
2060                     return
2061                             mostSpecificType == long.class
2062                             || mostSpecificType == int.class
2063                             || mostSpecificType == short.class
2064                             || mostSpecificType == byte.class;
2065                 }
2066                 else if (type == float.class) {
2067                     return
2068                             mostSpecificType == float.class
2069                             || mostSpecificType == int.class
2070                             || mostSpecificType == long.class
2071                             || mostSpecificType == short.class
2072                             || mostSpecificType == byte.class;
2073                 }
2074                 else if (type == short.class) {
2075                     return
2076                             mostSpecificType == short.class
2077                             || mostSpecificType == byte.class;
2078                 }
2079                 else {
2080                     return false;
2081                 }
2082             }
2083         }
2084 
2085         boolean answer = type.isAssignableFrom(mostSpecificType);
2086         if (!answer) {
2087             answer = autoboxType(type).isAssignableFrom(autoboxType(mostSpecificType));
2088         }
2089         return answer;
2090     }
2091 
2092     private Class autoboxType(Class type) {
2093         if (type.isPrimitive()) {
2094             if (type == int.class) {
2095                 return Integer.class;
2096             }
2097             else if (type == double.class) {
2098                 return Double.class;
2099             }
2100             else if (type == long.class) {
2101                 return Long.class;
2102             }
2103             else if (type == boolean.class) {
2104                 return Boolean.class;
2105             }
2106             else if (type == float.class) {
2107                 return Float.class;
2108             }
2109             else if (type == char.class) {
2110                 return Character.class;
2111             }
2112             else if (type == byte.class) {
2113                 return Byte.class;
2114             }
2115             else if (type == short.class) {
2116                 return Short.class;
2117             }
2118         }
2119         return type;
2120     }
2121 
2122     /***
2123      * Coerces any GString instances into Strings
2124      * 
2125      * @return true if some coercion was done. 
2126      */
2127     protected static boolean coerceGStrings(Object[] arguments) {
2128         boolean coerced = false;
2129         for (int i = 0, size = arguments.length; i < size; i++) {
2130             Object argument = arguments[i];
2131             if (argument instanceof GString) {
2132                 arguments[i] = argument.toString();
2133                 coerced = true;
2134             }
2135         }
2136         return coerced;
2137     }
2138 
2139     protected boolean isGenericSetMethod(MetaMethod method) {
2140         return (method.getName().equals("set"))
2141             && method.getParameterTypes().length == 2;
2142     }
2143 
2144     protected boolean isGenericGetMethod(MetaMethod method) {
2145         if (method.getName().equals("get")) {
2146             Class[] parameterTypes = method.getParameterTypes();
2147             return parameterTypes.length == 1 && parameterTypes[0] == String.class;
2148         }
2149         return false;
2150     }
2151 
2152     private void registerMethods(boolean instanceMethods) {
2153         Method[] methods = theClass.getMethods();
2154         for (int i = 0; i < methods.length; i++) {
2155             Method method = methods[i];
2156             if (MethodHelper.isStatic(method)) {
2157                 Class[] paramTypes = method.getParameterTypes();
2158                 if (paramTypes.length > 0) {
2159                     Class owner = paramTypes[0];
2160                     if (instanceMethods) {
2161                         registry.lookup(owner).addNewInstanceMethod(method);
2162                     } else {
2163                         registry.lookup(owner).addNewStaticMethod(method);
2164                     }
2165                 }
2166             }
2167         }
2168     }
2169 
2170     protected void registerStaticMethods() {
2171         registerMethods(false);
2172     }
2173 
2174     protected void registerInstanceMethods() {
2175         registerMethods(true);
2176     }
2177 
2178     protected String capitalize(String property) {
2179         return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
2180     }
2181 
2182     /***
2183      * Call this method when any mutation method is called, such as adding a new
2184      * method to this MetaClass so that any caching or bytecode generation can be
2185      * regenerated.
2186      */
2187     protected synchronized void onMethodChange() {
2188         reflector = null;
2189     }
2190     
2191     protected synchronized void checkInitialised() {
2192         if (!initialised) {
2193             initialised = true;
2194             addInheritedMethods();
2195         }
2196         if (reflector == null) {
2197             generateReflector();
2198         }
2199     }
2200 
2201     protected MetaMethod createMetaMethod(final Method method) {
2202         if (registry.useAccessible()) {
2203             AccessController.doPrivileged(new PrivilegedAction() {
2204                 public Object run() {
2205                     method.setAccessible(true);
2206                     return null;
2207                 }
2208             });
2209         }
2210         if (useReflection) {
2211             //log.warning("Creating reflection based dispatcher for: " + method);
2212             return new ReflectionMetaMethod(method);
2213         }
2214         MetaMethod answer = new MetaMethod(method);
2215         if (isValidReflectorMethod(answer)) {
2216             allMethods.add(answer);
2217             answer.setMethodIndex(allMethods.size());
2218         }
2219         else {
2220             //log.warning("Creating reflection based dispatcher for: " + method);
2221             answer = new ReflectionMetaMethod(method);
2222         }
2223         return answer;
2224     }
2225 
2226     protected boolean isValidReflectorMethod(MetaMethod method) {
2227         // We cannot use a reflector if the method is private, protected, or package accessible only.
2228         if (!method.isPublic()) {
2229             return false;
2230         }
2231         Class declaringClass = method.getDeclaringClass();
2232         if (!Modifier.isPublic(declaringClass.getModifiers())) {
2233             // lets see if this method is implemented on an interface
2234             List list = getInterfaceMethods();
2235             for (Iterator iter = list.iterator(); iter.hasNext();) {
2236                 MetaMethod aMethod = (MetaMethod) iter.next();
2237                 if (method.isSame(aMethod)) {
2238                     method.setInterfaceClass(aMethod.getDeclaringClass());
2239                     return true;
2240                 }
2241             }
2242             /*** todo */
2243             //log.warning("Cannot invoke method on protected/private class which isn't visible on an interface so must use reflection instead: " + method);
2244             return false;
2245         }
2246         return true;
2247     }
2248 
2249     protected void generateReflector() {
2250         reflector = loadReflector(allMethods);
2251         if (reflector == null) {
2252             throw new RuntimeException("Should have a reflector!");
2253         }
2254         // lets set the reflector on all the methods
2255         for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
2256             MetaMethod metaMethod = (MetaMethod) iter.next();
2257             //System.out.println("Setting reflector for method: " + metaMethod + " with index: " + metaMethod.getMethodIndex());
2258             metaMethod.setReflector(reflector);
2259         }
2260     }
2261 
2262     protected Reflector loadReflector(List methods) {
2263         ReflectorGenerator generator = new ReflectorGenerator(methods);
2264         String className = theClass.getName();
2265         String packagePrefix = "gjdk.";
2266         /*
2267         if (className.startsWith("java.")) {
2268             packagePrefix = "gjdk.";
2269         }
2270         */
2271         String name = packagePrefix + className + "_GroovyReflector";
2272         if (theClass.isArray()) {
2273             String componentName = theClass.getComponentType().getName();
2274             /*
2275             if (componentName.startsWith("java.")) {
2276                 packagePrefix = "gjdk.";
2277             }
2278             */
2279             name = packagePrefix + componentName + "_GroovyReflectorArray";
2280         }
2281         // lets see if its already loaded
2282         try {
2283             Class type = loadReflectorClass(name);
2284             return (Reflector) type.newInstance();
2285         }
2286         catch (AccessControlException ace) {
2287             //Don't ignore this exception type
2288             throw ace;
2289         } 
2290         catch (Exception e) {
2291             // lets ignore, lets generate it && load it
2292         }
2293 
2294         ClassWriter cw = new ClassWriter(true);
2295         generator.generate(cw, name);
2296 
2297         byte[] bytecode = cw.toByteArray();
2298 
2299         try {
2300             Class type = loadReflectorClass(name, bytecode);
2301             return (Reflector) type.newInstance();
2302         }
2303         catch (Exception e) {
2304             throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + e, e);
2305         }
2306     }
2307 
2308     protected Class loadReflectorClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
2309         ClassLoader loader = theClass.getClassLoader();
2310         if (loader instanceof GroovyClassLoader) {
2311             final GroovyClassLoader gloader = (GroovyClassLoader) loader;
2312             return (Class) AccessController.doPrivileged(new PrivilegedAction() {
2313                 public Object run() {
2314                     return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
2315                 }
2316             });
2317         }
2318         return registry.loadClass(loader, name, bytecode);
2319     }
2320 
2321     protected Class loadReflectorClass(String name) throws ClassNotFoundException {
2322         ClassLoader loader = theClass.getClassLoader();
2323         if (loader instanceof GroovyClassLoader) {
2324             GroovyClassLoader gloader = (GroovyClassLoader) loader;
2325             return gloader.loadClass(name);
2326         }
2327         return registry.loadClass(loader, name);
2328     }
2329 
2330     public List getMethods() {
2331         return allMethods;
2332     }
2333 
2334     public List getMetaMethods() {
2335         return (List) ((ArrayList)newGroovyMethodsList).clone();
2336     }
2337 
2338     protected synchronized List getInterfaceMethods() {
2339         if (interfaceMethods == null) {
2340             interfaceMethods = new ArrayList();
2341             Class type = theClass;
2342             while (type != null) {
2343                 Class[] interfaces = type.getInterfaces();
2344                 for (int i = 0; i < interfaces.length; i++) {
2345                     Class iface = interfaces[i];
2346                     Method[] methods = iface.getMethods();
2347                     addInterfaceMethods(interfaceMethods, methods);
2348                 }
2349                 type = type.getSuperclass();
2350             }
2351         }
2352         return interfaceMethods;
2353     }
2354 
2355     private void addInterfaceMethods(List list, Method[] methods) {
2356         for (int i = 0; i < methods.length; i++) {
2357             list.add(createMetaMethod(methods[i]));
2358         }
2359     }
2360 
2361     /***
2362      * param instance array to the type array
2363      * @param args
2364      * @return
2365      */
2366     Class[] convertToTypeArray(Object[] args) {
2367         if (args == null)
2368             return null;
2369         int s = args.length;
2370         Class[] ans = new Class[s];
2371         for (int i = 0; i < s; i++) {
2372             Object o = args[i];
2373             if (o != null) {
2374                 ans[i] = o.getClass();
2375             } else {
2376                 ans[i] = null;
2377             }
2378         }
2379         return ans;
2380     }
2381 
2382 }