View Javadoc

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