View Javadoc

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