KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > groovy > lang > MetaClass


1 /*
2  $Id: MetaClass.java,v 1.106 2005/06/05 22:27:12 dierk 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 JavaDoc;
49 import java.beans.EventSetDescriptor JavaDoc;
50 import java.beans.IntrospectionException JavaDoc;
51 import java.beans.Introspector JavaDoc;
52 import java.beans.PropertyDescriptor JavaDoc;
53 import java.lang.reflect.Array JavaDoc;
54 import java.lang.reflect.Constructor JavaDoc;
55 import java.lang.reflect.Field JavaDoc;
56 import java.lang.reflect.InvocationHandler JavaDoc;
57 import java.lang.reflect.InvocationTargetException JavaDoc;
58 import java.lang.reflect.Method JavaDoc;
59 import java.lang.reflect.Modifier JavaDoc;
60 import java.lang.reflect.Proxy JavaDoc;
61 import java.math.BigDecimal JavaDoc;
62 import java.math.BigInteger JavaDoc;
63 import java.net.URL JavaDoc;
64 import java.security.AccessControlException JavaDoc;
65 import java.security.AccessController JavaDoc;
66 import java.security.PrivilegedAction JavaDoc;
67 import java.security.PrivilegedActionException JavaDoc;
68 import java.security.PrivilegedExceptionAction JavaDoc;
69 import java.util.*;
70 import java.util.logging.Logger JavaDoc;
71
72 import org.codehaus.groovy.ast.ClassNode;
73 import org.codehaus.groovy.classgen.ReflectorGenerator;
74 import org.codehaus.groovy.control.CompilationUnit;
75 import org.codehaus.groovy.control.Phases;
76 import org.codehaus.groovy.control.CompilerConfiguration;
77 import org.codehaus.groovy.runtime.ClosureListener;
78 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
79 import org.codehaus.groovy.runtime.GroovyCategorySupport;
80 import org.codehaus.groovy.runtime.InvokerHelper;
81 import org.codehaus.groovy.runtime.InvokerInvocationException;
82 import org.codehaus.groovy.runtime.MethodClosure;
83 import org.codehaus.groovy.runtime.MethodHelper;
84 import org.codehaus.groovy.runtime.MethodKey;
85 import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
86 import org.codehaus.groovy.runtime.NewStaticMetaMethod;
87 import org.codehaus.groovy.runtime.ReflectionMetaMethod;
88 import org.codehaus.groovy.runtime.Reflector;
89 import org.codehaus.groovy.runtime.TemporaryMethodKey;
90 import org.codehaus.groovy.runtime.TransformMetaMethod;
91 import org.objectweb.asm.ClassVisitor;
92 import org.objectweb.asm.ClassWriter;
93
94 /**
95  * Allows methods to be dynamically added to existing classes at runtime
96  *
97  * @author <a HREF="mailto:james@coredevelopers.net">James Strachan</a>
98  * @author Guillaume Laforge
99  * @version $Revision: 1.106 $
100  */

101 public class MetaClass {
102
103     private static final Logger JavaDoc log = Logger.getLogger(MetaClass.class.getName());
104
105     public static final Object JavaDoc[] EMPTY_ARRAY = {
106     };
107     public static Class JavaDoc[] EMPTY_TYPE_ARRAY = {
108     };
109     protected static final Object JavaDoc[] ARRAY_WITH_NULL = { null };
110
111     private static boolean useReflection = false;
112
113     protected MetaClassRegistry registry;
114     protected Class JavaDoc theClass;
115     private ClassNode classNode;
116     private Map methodIndex = new HashMap();
117     private Map staticMethodIndex = new HashMap();
118     private List newGroovyMethodsList = new ArrayList();
119     //private Map propertyDescriptors = Collections.synchronizedMap(new HashMap());
120
private Map propertyMap = Collections.synchronizedMap(new HashMap());
121     private Map listeners = new HashMap();
122     private Map methodCache = Collections.synchronizedMap(new HashMap());
123     private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
124     private MetaMethod genericGetMethod;
125     private MetaMethod genericSetMethod;
126     private List constructors;
127     private List allMethods = new ArrayList();
128     private List interfaceMethods;
129     private Reflector reflector;
130     private boolean initialised;
131     // we only need one of these that can be reused over and over.
132
private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
133     
134     public MetaClass(MetaClassRegistry registry, final Class JavaDoc theClass) throws IntrospectionException JavaDoc {
135         this.registry = registry;
136         this.theClass = theClass;
137
138         constructors = Arrays.asList(theClass.getDeclaredConstructors());
139         addMethods(theClass,true);
140
141         // introspect
142
BeanInfo JavaDoc info = null;
143         try {
144             info =(BeanInfo JavaDoc) AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc() {
145                 public Object JavaDoc run() throws IntrospectionException JavaDoc {
146                     return Introspector.getBeanInfo(theClass);
147                 }
148             });
149         } catch (PrivilegedActionException JavaDoc pae) {
150             if (pae.getException() instanceof IntrospectionException JavaDoc) {
151                 throw (IntrospectionException JavaDoc) pae.getException();
152             } else {
153                 throw new RuntimeException JavaDoc(pae.getException());
154             }
155         }
156
157         PropertyDescriptor JavaDoc[] descriptors = info.getPropertyDescriptors();
158         
159         // build up the metaproperties based on the public fields, property descriptors,
160
// and the getters and setters
161
setupProperties(descriptors);
162         
163         /* old code
164         for (int i = 0; i < descriptors.length; i++) {
165             PropertyDescriptor descriptor = descriptors[i];
166             propertyDescriptors.put(descriptor.getName(), descriptor);
167         }
168         */

169         
170         EventSetDescriptor JavaDoc[] eventDescriptors = info.getEventSetDescriptors();
171         for (int i = 0; i < eventDescriptors.length; i++) {
172             EventSetDescriptor JavaDoc descriptor = eventDescriptors[i];
173             Method JavaDoc[] listenerMethods = descriptor.getListenerMethods();
174             for (int j = 0; j < listenerMethods.length; j++) {
175                 Method JavaDoc listenerMethod = listenerMethods[j];
176                 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
177                 listeners.put(listenerMethod.getName(), metaMethod);
178             }
179         }
180     }
181
182     public static boolean isUseReflection() {
183         return useReflection;
184     }
185
186     /**
187      * Allows reflection to be enabled in situations where bytecode generation
188      * of method invocations causes issues.
189      *
190      * @param useReflection
191      */

192     public static void setUseReflection(boolean useReflection) {
193         MetaClass.useReflection = useReflection;
194     }
195
196     private void addInheritedMethods() {
197         LinkedList superClasses = new LinkedList();
198         for (Class JavaDoc c = theClass.getSuperclass(); c!=Object JavaDoc.class && c!= null; c = c.getSuperclass()) {
199             superClasses.addFirst(c);
200         }
201         // lets add all the base class methods
202
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
203             Class JavaDoc c = (Class JavaDoc) iter.next();
204             addMethods(c,true);
205             addNewStaticMethodsFrom(c);
206         }
207
208         // now lets see if there are any methods on one of my interfaces
209
Class JavaDoc[] interfaces = theClass.getInterfaces();
210         for (int i = 0; i < interfaces.length; i++) {
211             addNewStaticMethodsFrom(interfaces[i]);
212         }
213
214         // lets add Object methods after interfaces, as all interfaces derive from Object.
215
// this ensures List and Collection methods come before Object etc
216
if (theClass != Object JavaDoc.class) {
217             addMethods(Object JavaDoc.class, false);
218             addNewStaticMethodsFrom(Object JavaDoc.class);
219         }
220
221         if (theClass.isArray() && !theClass.equals(Object JavaDoc[].class)) {
222             addNewStaticMethodsFrom(Object JavaDoc[].class);
223         }
224     }
225
226     /**
227      * @return all the normal instance methods avaiable on this class for the
228      * given name
229      */

230     public List getMethods(String JavaDoc name) {
231         List answer = (List) methodIndex.get(name);
232         List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
233         if (used != null) {
234             if (answer != null) {
235                 used.addAll(answer);
236             }
237             answer = used;
238         }
239         if (answer == null) {
240             answer = Collections.EMPTY_LIST;
241         }
242         return answer;
243     }
244
245     /**
246      * @return all the normal static methods avaiable on this class for the
247      * given name
248      */

249     public List getStaticMethods(String JavaDoc name) {
250         List answer = (List) staticMethodIndex.get(name);
251         if (answer == null) {
252             return Collections.EMPTY_LIST;
253         }
254         return answer;
255     }
256
257     /**
258      * Allows static method definitions to be added to a meta class as if it
259      * was an instance method
260      *
261      * @param method
262      */

263     protected void addNewInstanceMethod(Method JavaDoc method) {
264         if (initialised) {
265             throw new RuntimeException JavaDoc("Already initialized, cannot add new method: " + method);
266         }
267         else {
268             NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
269             addMethod(newMethod,false);
270             addNewInstanceMethod(newMethod);
271         }
272     }
273
274     protected void addNewInstanceMethod(MetaMethod method) {
275         newGroovyMethodsList.add(method);
276     }
277
278     protected void addNewStaticMethod(Method JavaDoc method) {
279         if (initialised) {
280             throw new RuntimeException JavaDoc("Already initialized, cannot add new method: " + method);
281         }
282         else {
283             NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
284             addMethod(newMethod,false);
285             addNewStaticMethod(newMethod);
286         }
287     }
288
289     protected void addNewStaticMethod(MetaMethod method) {
290         newGroovyMethodsList.add(method);
291     }
292
293     public Object JavaDoc invokeMethod(Object JavaDoc object, String JavaDoc methodName, Object JavaDoc arguments) {
294         return invokeMethod(object, methodName, asArray(arguments));
295     }
296
297     /**
298      * Invokes the given method on the object.
299      *
300      */

301     public Object JavaDoc invokeMethod(Object JavaDoc object, String JavaDoc methodName, Object JavaDoc[] arguments) {
302         if (object == null) {
303             throw new NullPointerException JavaDoc("Cannot invoke method: " + methodName + " on null object");
304         }
305
306         MetaMethod method = retrieveMethod(object, methodName, arguments);
307
308         if (method != null) {
309             return doMethodInvoke(object, method, arguments);
310         } else {
311             // if no method was found, try to find a closure defined as a field of the class and run it
312
try {
313                 Object JavaDoc value = this.getProperty(object, methodName);
314                 if (value instanceof Closure && object!=this) {
315                     Closure closure = (Closure) value;
316                     closure.setDelegate(this);
317                     return closure.call(arguments);
318                 }
319                 else {
320                     throw new MissingMethodException(methodName, theClass, arguments);
321                 }
322             }
323             catch (Exception JavaDoc e) {
324                 throw new MissingMethodException(methodName, theClass, arguments);
325             }
326         }
327     }
328
329     protected MetaMethod retrieveMethod(Object JavaDoc owner, String JavaDoc methodName, Object JavaDoc[] arguments) {
330         // lets try use the cache to find the method
331
MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
332         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
333         if (method == null) {
334             method = pickMethod(owner, methodName, arguments);
335             if (method != null && method.isCacheable()) {
336                 methodCache.put(methodKey.createCopy(), method);
337             }
338         }
339         return method;
340     }
341
342     public MetaMethod retrieveMethod(String JavaDoc methodName, Class JavaDoc[] arguments) {
343         // lets try use the cache to find the method
344
MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
345         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
346         if (method == null) {
347             method = pickMethod(methodName, arguments); // todo shall call pickStaticMethod also?
348
if (method != null && method.isCacheable()) {
349                 methodCache.put(methodKey.createCopy(), method);
350             }
351         }
352         return method;
353     }
354
355     public Constructor JavaDoc retrieveConstructor(Class JavaDoc[] arguments) {
356         Constructor JavaDoc constructor = (Constructor JavaDoc) chooseMethod("<init>", constructors, arguments, false);
357         if (constructor != null) {
358             return constructor;
359         }
360         else {
361             constructor = (Constructor JavaDoc) chooseMethod("<init>", constructors, arguments, true);
362             if (constructor != null) {
363                 return constructor;
364             }
365         }
366         return null;
367     }
368
369     public MetaMethod retrieveStaticMethod(String JavaDoc methodName, Class JavaDoc[] arguments) {
370         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
371         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
372         if (method == null) {
373             method = pickStaticMethod(methodName, arguments);
374             if (method != null) {
375                 staticMethodCache.put(methodKey.createCopy(), method);
376             }
377         }
378         return method;
379     }
380     /**
381      * Picks which method to invoke for the given object, method name and arguments
382      */

383     protected MetaMethod pickMethod(Object JavaDoc object, String JavaDoc methodName, Object JavaDoc[] arguments) {
384         MetaMethod method = null;
385         List methods = getMethods(methodName);
386         if (!methods.isEmpty()) {
387             Class JavaDoc[] argClasses = convertToTypeArray(arguments);
388             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
389             if (method == null) {
390                 int size = (arguments != null) ? arguments.length : 0;
391                 if (size == 1) {
392                     Object JavaDoc firstArgument = arguments[0];
393                     if (firstArgument instanceof List) {
394                         // lets coerce the list arguments into an array of
395
// arguments
396
// e.g. calling JFrame.setLocation( [100, 100] )
397

398                         List list = (List) firstArgument;
399                         arguments = list.toArray();
400                         argClasses = convertToTypeArray(arguments);
401                         method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
402                         if (method==null) return null;
403                             return new TransformMetaMethod(method) {
404                                 public Object JavaDoc invoke(Object JavaDoc object, Object JavaDoc[] arguments) throws Exception JavaDoc {
405                                     Object JavaDoc firstArgument = arguments[0];
406                                     List list = (List) firstArgument;
407                                     arguments = list.toArray();
408                                     return super.invoke(object, arguments);
409                                 }
410                             };
411                     }
412                 }
413             }
414         }
415         return method;
416     }
417
418     /**
419      * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
420      * this method is used only by ClassGenerator for static binding
421      * @param methodName
422      * @param arguments
423      * @return
424      */

425     protected MetaMethod pickMethod(String JavaDoc methodName, Class JavaDoc[] arguments) {
426         MetaMethod method = null;
427         List methods = getMethods(methodName);
428         if (!methods.isEmpty()) {
429             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
430 // no coersion at classgen time.
431
// if (method == null) {
432
// method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
433
// }
434
}
435         return method;
436     }
437
438     public Object JavaDoc invokeStaticMethod(Object JavaDoc object, String JavaDoc methodName, Object JavaDoc[] arguments) {
439         // System.out.println("Calling static method: " + methodName + " on args: " + InvokerHelper.toString(arguments));
440
// Class type = arguments == null ? null : arguments.getClass();
441
// System.out.println("Argument type: " + type);
442
// System.out.println("Type of first arg: " + arguments[0] + " type: " + arguments[0].getClass());
443

444         // lets try use the cache to find the method
445
MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
446         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
447         if (method == null) {
448             method = pickStaticMethod(object, methodName, arguments);
449             if (method != null) {
450                 staticMethodCache.put(methodKey.createCopy(), method);
451             }
452         }
453
454         if (method != null) {
455             return doMethodInvoke(object, method, arguments);
456         }
457         /*
458         List methods = getStaticMethods(methodName);
459         
460         if (!methods.isEmpty()) {
461             MetaMethod method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
462             if (method != null) {
463                 return doMethodInvoke(theClass, method, arguments);
464             }
465         }
466         
467         if (theClass != Class.class) {
468             try {
469                 return registry.getMetaClass(Class.class).invokeMethod(object, methodName, arguments);
470             }
471             catch (GroovyRuntimeException e) {
472                 // throw our own exception
473             }
474         }
475         */

476         throw new MissingMethodException(methodName, theClass, arguments);
477     }
478
479     protected MetaMethod pickStaticMethod(Object JavaDoc object, String JavaDoc methodName, Object JavaDoc[] arguments) {
480         MetaMethod method = null;
481         List methods = getStaticMethods(methodName);
482
483         if (!methods.isEmpty()) {
484             method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false);
485         }
486
487         if (method == null && theClass != Class JavaDoc.class) {
488             MetaClass classMetaClass = registry.getMetaClass(Class JavaDoc.class);
489             method = classMetaClass.pickMethod(object, methodName, arguments);
490         }
491         return method;
492     }
493
494     protected MetaMethod pickStaticMethod(String JavaDoc methodName, Class JavaDoc[] arguments) {
495         MetaMethod method = null;
496         List methods = getStaticMethods(methodName);
497
498         if (!methods.isEmpty()) {
499             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
500 // disabled to keep consistant with the original version of pickStatciMethod
501
// if (method == null) {
502
// method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
503
// }
504
}
505
506         if (method == null && theClass != Class JavaDoc.class) {
507             MetaClass classMetaClass = registry.getMetaClass(Class JavaDoc.class);
508             method = classMetaClass.pickMethod(methodName, arguments);
509         }
510         return method;
511     }
512
513     public Object JavaDoc invokeConstructor(Object JavaDoc[] arguments) {
514         Class JavaDoc[] argClasses = convertToTypeArray(arguments);
515         Constructor JavaDoc constructor = (Constructor JavaDoc) chooseMethod("<init>", constructors, argClasses, false);
516         if (constructor != null) {
517             return doConstructorInvoke(constructor, arguments);
518         }
519         else {
520             constructor = (Constructor JavaDoc) chooseMethod("<init>", constructors, argClasses, true);
521             if (constructor != null) {
522                 return doConstructorInvoke(constructor, arguments);
523             }
524         }
525
526         if (arguments.length == 1) {
527             Object JavaDoc firstArgument = arguments[0];
528             if (firstArgument instanceof Map) {
529                 constructor = (Constructor JavaDoc) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false);
530                 if (constructor != null) {
531                     Object JavaDoc bean = doConstructorInvoke(constructor, EMPTY_ARRAY);
532                     setProperties(bean, ((Map) firstArgument));
533                     return bean;
534                 }
535             }
536         }
537         throw new GroovyRuntimeException(
538                     "Could not find matching constructor for: "
539                         + theClass.getName()
540                         + "("+InvokerHelper.toTypeString(arguments)+")");
541     }
542
543     /**
544      * Sets a number of bean properties from the given Map where the keys are
545      * the String names of properties and the values are the values of the
546      * properties to set
547      */

548     public void setProperties(Object JavaDoc bean, Map map) {
549         for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
550             Map.Entry entry = (Map.Entry) iter.next();
551             String JavaDoc key = entry.getKey().toString();
552             
553             // do we have this property?
554
if(propertyMap.get(key) == null)
555                 continue;
556             
557             Object JavaDoc value = entry.getValue();
558             try {
559                 setProperty(bean, key, value);
560             }
561             catch (GroovyRuntimeException e) {
562                 // lets ignore missing properties
563
/** todo should replace this code with a getMetaProperty(key) != null check
564                  i.e. don't try and set a non-existent property
565                  */

566             }
567         }
568     }
569
570     /**
571      * @return the given property's value on the object
572      */

573     public Object JavaDoc getProperty(final Object JavaDoc object, final String JavaDoc property) {
574         // look for the property in our map
575
MetaProperty mp = (MetaProperty) propertyMap.get(property);
576         if(mp != null) {
577             try {
578                 //System.out.println("we found a metaproperty for " + theClass.getName() +
579
// "." + property);
580
// delegate the get operation to the metaproperty
581
return mp.getProperty(object);
582             }
583             catch(Exception JavaDoc e) {
584                 throw new GroovyRuntimeException("Cannot read property: " + property);
585             }
586         }
587
588         if (genericGetMethod == null) {
589             // Make sure there isn't a generic method in the "use" cases
590
List possibleGenericMethods = getMethods("get");
591             if (possibleGenericMethods != null) {
592                 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
593                     MetaMethod mmethod = (MetaMethod) i.next();
594                     Class JavaDoc[] paramTypes = mmethod.getParameterTypes();
595                     if (paramTypes.length == 1 && paramTypes[0] == String JavaDoc.class) {
596                         Object JavaDoc[] arguments = {property};
597                         Object JavaDoc answer = doMethodInvoke(object, mmethod, arguments);
598                         return answer;
599                     }
600                 }
601             }
602         }
603         else {
604             Object JavaDoc[] arguments = { property };
605             Object JavaDoc answer = doMethodInvoke(object, genericGetMethod, arguments);
606             // jes bug? a property retrieved via a generic get() can't have a null value?
607
if (answer != null) {
608                 return answer;
609             }
610         }
611
612         if (!CompilerConfiguration.isJsrGroovy()) {
613             // is the property the name of a method - in which case return a
614
// closure
615
List methods = getMethods(property);
616             if (!methods.isEmpty()) {
617                 return new MethodClosure(object, property);
618             }
619         }
620
621         // lets try invoke a static getter method
622
// this case is for protected fields. I wish there was a better way...
623
Exception JavaDoc lastException = null;
624         try {
625             MetaMethod method = findGetter(object, "get" + capitalize(property));
626             if (method != null) {
627                 return doMethodInvoke(object, method, EMPTY_ARRAY);
628             }
629         }
630         catch (GroovyRuntimeException e) {
631             lastException = e;
632         }
633
634         /** todo or are we an extensible groovy class? */
635         if (genericGetMethod != null) {
636             return null;
637         }
638         else {
639             /** todo these special cases should be special MetaClasses maybe */
640             if (object instanceof Class JavaDoc) {
641                 // lets try a static field
642
return getStaticProperty((Class JavaDoc) object, property);
643             }
644             if (object instanceof Collection) {
645                 return DefaultGroovyMethods.getAt((Collection) object, property);
646             }
647             if (object instanceof Object JavaDoc[]) {
648                 return DefaultGroovyMethods.getAt(Arrays.asList((Object JavaDoc[]) object), property);
649             }
650             if (object instanceof Object JavaDoc) {
651                 Field JavaDoc field = null;
652                 try {
653                     // lets try a public field
654
field = object.getClass().getDeclaredField(property);
655                     return field.get(object);
656                 } catch (IllegalAccessException JavaDoc iae) {
657                     lastException = new IllegalPropertyAccessException(field,object.getClass());
658                 } catch (Exception JavaDoc e1) {
659                     // fall through
660
}
661             }
662             
663             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
664             if (addListenerMethod != null) {
665                 /* @todo one day we could try return the previously registered Closure listener for easy removal */
666                 return null;
667             }
668
669             if (lastException == null)
670                 throw new MissingPropertyException(property, theClass);
671             else
672                 throw new MissingPropertyException(property, theClass, lastException);
673         }
674     }
675
676     /**
677      * Get all the properties defined for this type
678      * @return a list of MetaProperty objects
679      */

680     public List getProperties() {
681         // simply return the values of the metaproperty map as a List
682
return new ArrayList(propertyMap.values());
683     }
684     
685     /**
686      * This will build up the property map (Map of MetaProperty objects, keyed on
687      * property name).
688      */

689     protected void setupProperties(PropertyDescriptor JavaDoc[] propertyDescriptors) {
690         MetaProperty mp;
691         Method JavaDoc method;
692         MetaMethod getter = null;
693         MetaMethod setter = null;
694         Class JavaDoc klass;
695         
696         // first get the public fields and create MetaFieldProperty objects
697
klass = theClass;
698         while(klass != null) {
699             Field JavaDoc[] fields = klass.getDeclaredFields();
700             for(int i = 0; i < fields.length; i++) {
701                 // we're only interested in publics
702
if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0)
703                     continue;
704                 
705                 // see if we already got this
706
if(propertyMap.get(fields[i].getName()) != null)
707                     continue;
708                 
709                 //System.out.println("adding field " + fields[i].getName() +
710
// " for class " + klass.getName());
711
// stick it in there!
712
propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
713             }
714             
715             // now get the super class
716
klass = klass.getSuperclass();
717         }
718         
719         // if this an Array, then add the special read-only "length" property
720
if(theClass.isArray()) {
721             propertyMap.put("length", arrayLengthProperty);
722         }
723         
724         // now iterate over the map of property descriptors and generate
725
// MetaBeanProperty objects
726
for(int i=0; i<propertyDescriptors.length; i++) {
727             PropertyDescriptor JavaDoc pd = propertyDescriptors[i];
728             // skip if the field already exists in the map
729
if(propertyMap.get(pd.getName()) != null)
730                 continue;
731             
732             // skip if the property type is unknown (this seems to be the case if the
733
// property descriptor is based on a setX() method that has two parameters,
734
// which is not a valid property)
735
if(pd.getPropertyType() == null)
736                 continue;
737             
738             // get the getter method
739
method = pd.getReadMethod();
740             if(method != null)
741                 getter = findMethod(method);
742             else
743                 getter = null;
744             
745             // get the setter method
746
method = pd.getWriteMethod();
747             if(method != null)
748                 setter = findMethod(method);
749             else
750                 setter = null;
751             
752             // now create the MetaProperty object
753
//System.out.println("creating a bean property for class " +
754
// theClass.getName() + ": " + pd.getName());
755

756             mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
757             
758             // put it in the list
759
propertyMap.put(pd.getName(), mp);
760         }
761         
762         // now look for any stray getters that may be used to define a property
763
klass = theClass;
764         while(klass != null) {
765             Method JavaDoc[] methods = klass.getDeclaredMethods();
766             for (int i = 0; i < methods.length; i++) {
767                 // filter out the privates
768
if(Modifier.isPublic(methods[i].getModifiers()) == false)
769                     continue;
770                 
771                 method = methods[i];
772                 
773                 String JavaDoc methodName = method.getName();
774                 
775                 // is this a getter?
776
if(methodName.startsWith("get") &&
777                     methodName.length() > 3 &&
778                     method.getParameterTypes().length == 0) {
779                     
780                     // get the name of the property
781
String JavaDoc propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
782                     
783                     // is this property already accounted for?
784
mp = (MetaProperty) propertyMap.get(propName);
785                     if(mp != null) {
786                         // we may have already found the setter for this
787
if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
788                             // update the getter method to this one
789
((MetaBeanProperty) mp).setGetter(findMethod(method));
790                         }
791                     }
792                     else {
793                         // we need to create a new property object
794
// type of the property is what the get method returns
795
MetaBeanProperty mbp = new MetaBeanProperty(propName,
796                             method.getReturnType(),
797                             findMethod(method), null);
798                             
799                         // add it to the map
800
propertyMap.put(propName, mbp);
801                     }
802                 }
803                 else if(methodName.startsWith("set") &&
804                     methodName.length() > 3 &&
805                     method.getParameterTypes().length == 1) {
806                     
807                     // get the name of the property
808
String JavaDoc propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
809                     
810                     // did we already find the getter of this?
811
mp = (MetaProperty) propertyMap.get(propName);
812                     if(mp != null) {
813                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
814                             // update the setter method to this one
815
((MetaBeanProperty) mp).setSetter(findMethod(method));
816                         }
817                     }
818                     else {
819                         // this is a new property to add
820
MetaBeanProperty mbp = new MetaBeanProperty(propName,
821                                                                     method.getParameterTypes()[0],
822                                                                     null,
823                                                                     findMethod(method));
824                             
825                         // add it to the map
826
propertyMap.put(propName, mbp);
827                     }
828                 }
829             }
830             
831             // now get the super class
832
klass = klass.getSuperclass();
833         }
834     }
835     
836     /**
837      * Sets the property value on an object
838      */

839     public void setProperty(Object JavaDoc object, String JavaDoc property, Object JavaDoc newValue) {
840         MetaProperty mp = (MetaProperty) propertyMap.get(property);
841         if(mp != null) {
842             try {
843                 mp.setProperty(object, newValue);
844                 return;
845             }
846             catch(ReadOnlyPropertyException e) {
847                 // just rethrow it; there's nothing left to do here
848
throw e;
849             }
850             catch (TypeMismatchException e) {
851                 // tried to access to mismatched object.
852
throw e;
853             }
854             catch (Exception JavaDoc e) {
855                 // if the value is a List see if we can construct the value
856
// from a constructor
857
if (newValue == null)
858                     return;
859                 if (newValue instanceof List) {
860                     List list = (List) newValue;
861                     int params = list.size();
862                     Constructor JavaDoc[] constructors = mp.getType().getConstructors();
863                     for (int i = 0; i < constructors.length; i++) {
864                         Constructor JavaDoc constructor = constructors[i];
865                         if (constructor.getParameterTypes().length == params) {
866                             Object JavaDoc value = doConstructorInvoke(constructor, list.toArray());
867                             mp.setProperty(object, value);
868                             return;
869                         }
870                     }
871                     
872                     // if value is an array
873
Class JavaDoc parameterType = mp.getType();
874                     if (parameterType.isArray()) {
875                         Object JavaDoc objArray = asPrimitiveArray(list, parameterType);
876                         mp.setProperty(object, objArray);
877                         return;
878                     }
879                 }
880
881                 // if value is an multidimensional array
882
// jes currently this logic only supports metabeansproperties and
883
// not metafieldproperties. It shouldn't be too hard to support
884
// the latter...
885
if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
886                     MetaBeanProperty mbp = (MetaBeanProperty) mp;
887                     List list = Arrays.asList((Object JavaDoc[])newValue);
888                     MetaMethod setter = mbp.getSetter();
889                     
890                     Class JavaDoc parameterType = setter.getParameterTypes()[0];
891                     Class JavaDoc arrayType = parameterType.getComponentType();
892                     Object JavaDoc objArray = Array.newInstance(arrayType, list.size());
893                     
894                     for (int i = 0; i < list.size(); i++) {
895                         List list2 =Arrays.asList((Object JavaDoc[]) list.get(i));
896                         Object JavaDoc objArray2 = asPrimitiveArray(list2, arrayType);
897                         Array.set(objArray, i, objArray2);
898                     }
899
900                     doMethodInvoke(object, setter, new Object JavaDoc[]{
901                         objArray
902                     });
903                     return;
904                 }
905                 
906                 throw new MissingPropertyException(property, theClass, e);
907             }
908         }
909         
910         try {
911             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
912             if (addListenerMethod != null && newValue instanceof Closure) {
913                 // lets create a dynamic proxy
914
Object JavaDoc proxy =
915                     createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
916                 doMethodInvoke(object, addListenerMethod, new Object JavaDoc[] { proxy });
917                 return;
918             }
919
920             if (genericSetMethod == null) {
921                 // Make sure there isn't a generic method in the "use" cases
922
List possibleGenericMethods = getMethods("set");
923                 if (possibleGenericMethods != null) {
924                     for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
925                         MetaMethod mmethod = (MetaMethod) i.next();
926                         Class JavaDoc[] paramTypes = mmethod.getParameterTypes();
927                         if (paramTypes.length == 2 && paramTypes[0] == String JavaDoc.class) {
928                             Object JavaDoc[] arguments = {property, newValue};
929                             Object JavaDoc answer = doMethodInvoke(object, mmethod, arguments);
930                             return;
931                         }
932                     }
933                 }
934             }
935             else {
936                 Object JavaDoc[] arguments = { property, newValue };
937                 doMethodInvoke(object, genericSetMethod, arguments);
938                 return;
939             }
940
941             /** todo or are we an extensible class? */
942
943             // lets try invoke the set method
944
// this is kind of ugly: if it is a protected field, we fall
945
// all the way down to this klunky code. Need a better
946
// way to handle this situation...
947

948             String JavaDoc method = "set" + capitalize(property);
949             try {
950                 invokeMethod(object, method, new Object JavaDoc[] { newValue });
951             }
952             catch (MissingMethodException e1) {
953                 Field JavaDoc field = null;
954                 try {
955                     field = object.getClass().getDeclaredField(property);
956                     //field.setAccessible(true);
957
field.set(object, newValue);
958                 } catch (IllegalAccessException JavaDoc iae) {
959                     throw new IllegalPropertyAccessException(field,object.getClass());
960                 } catch (Exception JavaDoc e2) {
961                     throw new MissingPropertyException(property, theClass, e2);
962                 }
963             }
964             
965         }
966         catch (GroovyRuntimeException e) {
967             throw new MissingPropertyException(property, theClass, e);
968         }
969         
970     }
971
972
973     /**
974      * Looks up the given attribute (field) on the given object
975      */

976     public Object JavaDoc getAttribute(Object JavaDoc object, String JavaDoc attribute) {
977         try {
978             Field JavaDoc field = theClass.getDeclaredField(attribute);
979             field.setAccessible(true);
980             return field.get(object);
981         }
982         catch (NoSuchFieldException JavaDoc e) {
983             throw new MissingFieldException(attribute, theClass);
984         }
985         catch (IllegalAccessException JavaDoc e) {
986             throw new MissingFieldException(attribute, theClass, e);
987         }
988     }
989
990     /**
991      * Sets the given attribute (field) on the given object
992      */

993     public void setAttribute(Object JavaDoc object, String JavaDoc attribute, Object JavaDoc newValue) {
994         try {
995             Field JavaDoc field = theClass.getDeclaredField(attribute);
996             field.setAccessible(true);
997             field.set(object, newValue);
998         }
999         catch (NoSuchFieldException JavaDoc e) {
1000            throw new MissingFieldException(attribute, theClass);
1001        }
1002        catch (IllegalAccessException JavaDoc e) {
1003            throw new MissingFieldException(attribute, theClass, e);
1004        }
1005    }
1006
1007    /**
1008     * Returns a callable object for the given method name on the object.
1009     * The object acts like a Closure in that it can be called, like a closure
1010     * and passed around - though really its a method pointer, not a closure per se.
1011     */

1012    public Closure getMethodPointer(Object JavaDoc object, String JavaDoc methodName) {
1013        return new MethodClosure(object, methodName);
1014    }
1015
1016    /**
1017     * @param list
1018     * @param parameterType
1019     * @return
1020     */

1021    private Object JavaDoc asPrimitiveArray(List list, Class JavaDoc parameterType) {
1022        Class JavaDoc arrayType = parameterType.getComponentType();
1023        Object JavaDoc objArray = Array.newInstance(arrayType, list.size());
1024        for (int i = 0; i < list.size(); i++) {
1025            Object JavaDoc obj = list.get(i);
1026            if (arrayType.isPrimitive()) {
1027                if (obj instanceof Integer JavaDoc) {
1028                    Array.setInt(objArray, i, ((Integer JavaDoc) obj).intValue());
1029                }
1030                else if (obj instanceof Double JavaDoc) {
1031                    Array.setDouble(objArray, i, ((Double JavaDoc) obj).doubleValue());
1032                }
1033                else if (obj instanceof Boolean JavaDoc) {
1034                    Array.setBoolean(objArray, i, ((Boolean JavaDoc) obj).booleanValue());
1035                }
1036                else if (obj instanceof Long JavaDoc) {
1037                    Array.setLong(objArray, i, ((Long JavaDoc) obj).longValue());
1038                }
1039                else if (obj instanceof Float JavaDoc) {
1040                    Array.setFloat(objArray, i, ((Float JavaDoc) obj).floatValue());
1041                }
1042                else if (obj instanceof Character JavaDoc) {
1043                    Array.setChar(objArray, i, ((Character JavaDoc) obj).charValue());
1044                }
1045                else if (obj instanceof Byte JavaDoc) {
1046                    Array.setByte(objArray, i, ((Byte JavaDoc) obj).byteValue());
1047                }
1048                else if (obj instanceof Short JavaDoc) {
1049                    Array.setShort(objArray, i, ((Short JavaDoc) obj).shortValue());
1050                }
1051            }
1052            else {
1053                Array.set(objArray, i, obj);
1054            }
1055        }
1056        return objArray;
1057    }
1058    
1059    public ClassNode getClassNode() {
1060        if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
1061            // lets try load it from the classpath
1062
String JavaDoc className = theClass.getName();
1063            String JavaDoc groovyFile = className;
1064            int idx = groovyFile.indexOf('$');
1065            if (idx > 0) {
1066                groovyFile = groovyFile.substring(0, idx);
1067            }
1068            groovyFile = groovyFile.replace('.', '/') + ".groovy";
1069
1070            //System.out.println("Attempting to load: " + groovyFile);
1071
URL JavaDoc url = theClass.getClassLoader().getResource(groovyFile);
1072            if (url == null) {
1073                url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
1074            }
1075            if (url != null) {
1076                try {
1077
1078                    /**
1079                     * todo there is no CompileUnit in scope so class name
1080                     * checking won't work but that mostly affects the bytecode
1081                     * generation rather than viewing the AST
1082                     */

1083
1084                    CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1085                        public void call( ClassVisitor writer, ClassNode node ) {
1086                            if( node.getName().equals(theClass.getName()) ) {
1087                                MetaClass.this.classNode = node;
1088                            }
1089                        }
1090                    };
1091                    
1092                    
1093                    CompilationUnit unit = new CompilationUnit( getClass().getClassLoader() );
1094                    unit.setClassgenCallback( search );
1095                    unit.addSource( url );
1096                    unit.compile( Phases.CLASS_GENERATION );
1097                }
1098                catch (Exception JavaDoc e) {
1099                    throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
1100                }
1101            }
1102
1103        }
1104        return classNode;
1105    }
1106
1107    public String JavaDoc toString() {
1108        return super.toString() + "[" + theClass + "]";
1109    }
1110
1111    // Implementation methods
1112
//-------------------------------------------------------------------------
1113

1114    /**
1115     * Converts the given object into an array; if its an array then just cast
1116     * otherwise wrap it in an array
1117     */

1118    protected Object JavaDoc[] asArray(Object JavaDoc arguments) {
1119        if (arguments == null) {
1120            return EMPTY_ARRAY;
1121        }
1122        if (arguments instanceof Tuple) {
1123            Tuple tuple = (Tuple) arguments;
1124            return tuple.toArray();
1125        }
1126        if (arguments instanceof Object JavaDoc[]) {
1127            return (Object JavaDoc[]) arguments;
1128        }
1129        else {
1130            return new Object JavaDoc[] { arguments };
1131        }
1132    }
1133    
1134    /**
1135     * @param listenerType
1136     * the interface of the listener to proxy
1137     * @param listenerMethodName
1138     * the name of the method in the listener API to call the
1139     * closure on
1140     * @param closure
1141     * the closure to invoke on the listenerMethodName method
1142     * invocation
1143     * @return a dynamic proxy which calls the given closure on the given
1144     * method name
1145     */

1146    protected Object JavaDoc createListenerProxy(Class JavaDoc listenerType, final String JavaDoc listenerMethodName, final Closure closure) {
1147        InvocationHandler JavaDoc handler = new ClosureListener(listenerMethodName, closure);
1148        return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class JavaDoc[] { listenerType }, handler);
1149    }
1150
1151    /**
1152     * Adds all the methods declared in the given class to the metaclass
1153     * ignoring any matching methods already defined by a derived class
1154     *
1155     * @param theClass
1156     */

1157    protected void addMethods(Class JavaDoc theClass, boolean forceOverwrite) {
1158        // add methods directly declared in the class
1159
Method JavaDoc[] methodArray = theClass.getDeclaredMethods();
1160        for (int i = 0; i < methodArray.length; i++) {
1161            Method JavaDoc reflectionMethod = methodArray[i];
1162            if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
1163                continue;
1164            }
1165            MetaMethod method = createMetaMethod(reflectionMethod);
1166            addMethod(method,forceOverwrite);
1167        }
1168    }
1169
1170    protected void addMethod(MetaMethod method, boolean forceOverwrite) {
1171        String JavaDoc name = method.getName();
1172
1173        //System.out.println(theClass.getName() + " == " + name + Arrays.asList(method.getParameterTypes()));
1174

1175        if (isGenericGetMethod(method) && genericGetMethod == null) {
1176            genericGetMethod = method;
1177        }
1178        else if (isGenericSetMethod(method) && genericSetMethod == null) {
1179            genericSetMethod = method;
1180        }
1181        if (method.isStatic()) {
1182            List list = (List) staticMethodIndex.get(name);
1183            if (list == null) {
1184                list = new ArrayList();
1185                staticMethodIndex.put(name, list);
1186                list.add(method);
1187            }
1188            else {
1189                if (!containsMatchingMethod(list, method)) {
1190                    list.add(method);
1191                }
1192            }
1193        }
1194
1195        List list = (List) methodIndex.get(name);
1196        if (list == null) {
1197            list = new ArrayList();
1198            methodIndex.put(name, list);
1199            list.add(method);
1200        }
1201        else {
1202            if (forceOverwrite) {
1203                removeMatchingMethod(list,method);
1204                list.add(method);
1205            } else if (!containsMatchingMethod(list, method)) {
1206                list.add(method);
1207            }
1208        }
1209    }
1210
1211    /**
1212     * @return true if a method of the same matching prototype was found in the
1213     * list
1214     */

1215    protected boolean containsMatchingMethod(List list, MetaMethod method) {
1216        for (Iterator iter = list.iterator(); iter.hasNext();) {
1217            MetaMethod aMethod = (MetaMethod) iter.next();
1218            Class JavaDoc[] params1 = aMethod.getParameterTypes();
1219            Class JavaDoc[] params2 = method.getParameterTypes();
1220            if (params1.length == params2.length) {
1221                boolean matches = true;
1222                for (int i = 0; i < params1.length; i++) {
1223                    if (params1[i] != params2[i]) {
1224                        matches = false;
1225                        break;
1226                    }
1227                }
1228                if (matches) {
1229                    return true;
1230                }
1231            }
1232        }
1233        return false;
1234    }
1235    
1236    /**
1237     * remove a method of the same matching prototype was found in the list
1238     */

1239    protected void removeMatchingMethod(List list, MetaMethod method) {
1240        for (Iterator iter = list.iterator(); iter.hasNext();) {
1241            MetaMethod aMethod = (MetaMethod) iter.next();
1242            Class JavaDoc[] params1 = aMethod.getParameterTypes();
1243            Class JavaDoc[] params2 = method.getParameterTypes();
1244            if (params1.length == params2.length) {
1245                boolean matches = true;
1246                for (int i = 0; i < params1.length; i++) {
1247                    if (params1[i] != params2[i]) {
1248                        matches = false;
1249                        break;
1250                    }
1251                }
1252                if (matches) {
1253                    iter.remove();
1254                    return;
1255                }
1256            }
1257        }
1258        return;
1259    }
1260    
1261
1262    /**
1263     * Adds all of the newly defined methods from the given class to this
1264     * metaclass
1265     *
1266     * @param theClass
1267     */

1268    protected void addNewStaticMethodsFrom(Class JavaDoc theClass) {
1269        MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
1270        Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
1271        while (iter.hasNext()) {
1272            MetaMethod method = (MetaMethod) iter.next();
1273            addMethod(method,false);
1274            newGroovyMethodsList.add(method);
1275        }
1276    }
1277
1278    /**
1279     * @return the value of the static property of the given class
1280     */

1281    protected Object JavaDoc getStaticProperty(Class JavaDoc aClass, String JavaDoc property) {
1282        //System.out.println("Invoking property: " + property + " on class: "
1283
// + aClass);
1284

1285        Exception JavaDoc lastException = null;
1286        try {
1287            Field JavaDoc field = aClass.getField(property);
1288            if (field != null) {
1289                if ((field.getModifiers() & Modifier.STATIC) != 0) {
1290                    return field.get(null);
1291                }
1292            }
1293        }
1294        catch (Exception JavaDoc e) {
1295            lastException = e;
1296        }
1297
1298        // lets try invoke a static getter method
1299
try {
1300            MetaMethod method = findStaticGetter(aClass, "get" + capitalize(property));
1301            if (method != null) {
1302                return doMethodInvoke(aClass, method, EMPTY_ARRAY);
1303            }
1304        }
1305        catch (GroovyRuntimeException e) {
1306            throw new MissingPropertyException(property, aClass, e);
1307        }
1308
1309        if (lastException == null) {
1310            throw new MissingPropertyException(property, aClass);
1311        }
1312        else {
1313            throw new MissingPropertyException(property, aClass, lastException);
1314        }
1315    }
1316
1317    /**
1318     * @return the matching method which should be found
1319     */

1320    protected MetaMethod findMethod(Method JavaDoc aMethod) {
1321        List methods = getMethods(aMethod.getName());
1322        for (Iterator iter = methods.iterator(); iter.hasNext();) {
1323            MetaMethod method = (MetaMethod) iter.next();
1324            if (method.isMethod(aMethod)) {
1325                return method;
1326            }
1327        }
1328        //log.warning("Creating reflection based dispatcher for: " + aMethod);
1329
return new ReflectionMetaMethod(aMethod);
1330    }
1331
1332    /**
1333     * @return the getter method for the given object
1334     */

1335    protected MetaMethod findGetter(Object JavaDoc object, String JavaDoc name) {
1336        List methods = getMethods(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        return null;
1344    }
1345
1346    /**
1347     * @return the Method of the given name with no parameters or null
1348     */

1349    protected MetaMethod findStaticGetter(Class JavaDoc type, String JavaDoc name) {
1350        List methods = getStaticMethods(name);
1351        for (Iterator iter = methods.iterator(); iter.hasNext();) {
1352            MetaMethod method = (MetaMethod) iter.next();
1353            if (method.getParameterTypes().length == 0) {
1354                return method;
1355            }
1356        }
1357
1358        /** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1359        try {
1360            Method JavaDoc method = type.getMethod(name, EMPTY_TYPE_ARRAY);
1361            if ((method.getModifiers() & Modifier.STATIC) != 0) {
1362                return findMethod(method);
1363            }
1364            else {
1365                return null;
1366            }
1367        }
1368        catch (Exception JavaDoc e) {
1369            return null;
1370        }
1371    }
1372
1373    protected Object JavaDoc doMethodInvoke(Object JavaDoc object, MetaMethod method, Object JavaDoc[] argumentArray) {
1374        //System.out.println("Evaluating method: " + method);
1375
//System.out.println("on object: " + object + " with arguments: " +
1376
// InvokerHelper.toString(argumentArray));
1377
//System.out.println(this.theClass);
1378

1379        try {
1380            if (argumentArray == null) {
1381                argumentArray = EMPTY_ARRAY;
1382            }
1383            else if (method.getParameterTypes().length == 1 && argumentArray.length == 0) {
1384                argumentArray = ARRAY_WITH_NULL;
1385            }
1386            return method.invoke(object, argumentArray);
1387        }
1388        catch (ClassCastException JavaDoc e) {
1389            if (coerceGStrings(argumentArray)) {
1390                try {
1391                    return doMethodInvoke(object, method, argumentArray);
1392                }
1393                catch (Exception JavaDoc e2) {
1394                    // allow fall through
1395
}
1396            }
1397            throw new GroovyRuntimeException(
1398                "failed to invoke method: "
1399                    + method
1400                    + " on: "
1401                    + object
1402                    + " with arguments: "
1403                    + InvokerHelper.toString(argumentArray)
1404                    + " reason: "
1405                    + e,
1406                e);
1407        }
1408        catch (InvocationTargetException JavaDoc e) {
1409            /*Throwable t = e.getTargetException();
1410            if (t instanceof Error) {
1411                Error error = (Error) t;
1412                throw error;
1413            }
1414            if (t instanceof RuntimeException) {
1415                RuntimeException runtimeEx = (RuntimeException) t;
1416                throw runtimeEx;
1417            }*/

1418            throw new InvokerInvocationException(e);
1419        }
1420        catch (IllegalAccessException JavaDoc e) {
1421            throw new GroovyRuntimeException(
1422                "could not access method: "
1423                    + method
1424                    + " on: "
1425                    + object
1426                    + " with arguments: "
1427                    + InvokerHelper.toString(argumentArray)
1428                    + " reason: "
1429                    + e,
1430                e);
1431        }
1432        catch (IllegalArgumentException JavaDoc e) {
1433            if (coerceGStrings(argumentArray)) {
1434                try {
1435                    return doMethodInvoke(object, method, argumentArray);
1436                }
1437                catch (Exception JavaDoc e2) {
1438                    // allow fall through
1439
}
1440            }
1441            Object JavaDoc[] args = coerceNumbers(method, argumentArray);
1442            if (args != null && !Arrays.equals(argumentArray,args)) {
1443                try {
1444                    return doMethodInvoke(object, method, args);
1445                }
1446                catch (Exception JavaDoc e3) {
1447                    // allow fall through
1448
}
1449            }
1450            throw new GroovyRuntimeException(
1451                    "failed to invoke method: "
1452                    + method
1453                    + " on: "
1454                    + object
1455                    + " with arguments: "
1456                    + InvokerHelper.toString(argumentArray)
1457                    + "reason: "
1458                    + e
1459            );
1460        }
1461        catch (RuntimeException JavaDoc e) {
1462            throw e;
1463        }
1464        catch (Exception JavaDoc e) {
1465            throw new GroovyRuntimeException(
1466                "failed to invoke method: "
1467                    + method
1468                    + " on: "
1469                    + object
1470                    + " with arguments: "
1471                    + InvokerHelper.toString(argumentArray)
1472                    + " reason: "
1473                    + e,
1474                e);
1475        }
1476    }
1477
1478    private static Object JavaDoc[] coerceNumbers(MetaMethod method, Object JavaDoc[] arguments) {
1479        Object JavaDoc[] ans = null;
1480        boolean coerced = false; // to indicate that at least one param is coerced
1481

1482        Class JavaDoc[] params = method.getParameterTypes();
1483
1484        if (params.length != arguments.length) {
1485            return null;
1486        }
1487
1488        ans = new Object JavaDoc[arguments.length];
1489
1490        for (int i = 0, size = arguments.length; i < size; i++) {
1491            Object JavaDoc argument = arguments[i];
1492            Class JavaDoc param = params[i];
1493            if ((Number JavaDoc.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number JavaDoc) { // Number types
1494
if (param == Byte JavaDoc.class || param == Byte.TYPE ) {
1495                    ans[i] = new Byte JavaDoc(((Number JavaDoc)argument).byteValue());
1496                    coerced = true; continue;
1497                }
1498                if (param == Double JavaDoc.class || param == Double.TYPE) {
1499                    ans[i] = new Double JavaDoc(((Number JavaDoc)argument).doubleValue());
1500                    coerced = true; continue;
1501                }
1502                if (param == Float JavaDoc.class || param == Float.TYPE) {
1503                    ans[i] = new Float JavaDoc(((Number JavaDoc)argument).floatValue());
1504                    coerced = true; continue;
1505                }
1506                if (param == Integer JavaDoc.class || param == Integer.TYPE) {
1507                    ans[i] = new Integer JavaDoc(((Number JavaDoc)argument).intValue());
1508                    coerced = true; continue;
1509                }
1510                if (param == Long JavaDoc.class || param == Long.TYPE) {
1511                    ans[i] = new Long JavaDoc(((Number JavaDoc)argument).longValue());
1512                    coerced = true; continue;
1513                }
1514                if (param == Short JavaDoc.class || param == Short.TYPE) {
1515                    ans[i] = new Short JavaDoc(((Number JavaDoc)argument).shortValue());
1516                    coerced = true; continue;
1517                }
1518                if (param == BigDecimal JavaDoc.class ) {
1519                    ans[i] = new BigDecimal JavaDoc(((Number JavaDoc)argument).doubleValue());
1520                    coerced = true; continue;
1521                }
1522                if (param == BigInteger JavaDoc.class) {
1523                    ans[i] = new BigInteger JavaDoc(String.valueOf(((Number JavaDoc)argument).longValue()));
1524                    coerced = true; continue;
1525                }
1526            }
1527            else if (param.isArray() && argument.getClass().isArray()) {
1528                Class JavaDoc paramElem = param.getComponentType();
1529                if (paramElem.isPrimitive()) {
1530                    if (paramElem == boolean.class && argument.getClass().getName().equals("[Ljava.lang.Boolean;")) {
1531                        ans[i] = InvokerHelper.convertToBooleanArray(argument);
1532                        coerced = true;
1533                        continue;
1534                    }
1535                    if (paramElem == byte.class && argument.getClass().getName().equals("[Ljava.lang.Byte;")) {
1536                        ans[i] = InvokerHelper.convertToByteArray(argument);
1537                        coerced = true;
1538                        continue;
1539                    }
1540                    if (paramElem == char.class && argument.getClass().getName().equals("[Ljava.lang.Character;")) {
1541                        ans[i] = InvokerHelper.convertToCharArray(argument);
1542                        coerced = true;
1543                        continue;
1544                    }
1545                    if (paramElem == short.class && argument.getClass().getName().equals("[Ljava.lang.Short;")) {
1546                        ans[i] = InvokerHelper.convertToShortArray(argument);
1547                        coerced = true;
1548                        continue;
1549                    }
1550                    if (paramElem == int.class && argument.getClass().getName().equals("[Ljava.lang.Integer;")) {
1551                        ans[i] = InvokerHelper.convertToIntArray(argument);
1552                        coerced = true;
1553                        continue;
1554                    }
1555                    if (paramElem == long.class
1556                            && argument.getClass().getName().equals("[Ljava.lang.Long;")
1557                            && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1558                                                            ) {
1559                        ans[i] = InvokerHelper.convertToLongArray(argument);
1560                        coerced = true;
1561                        continue;
1562                    }
1563                    if (paramElem == float.class
1564                            && argument.getClass().getName().equals("[Ljava.lang.Float;")
1565                            && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1566                                                            ) {
1567                        ans[i] = InvokerHelper.convertToFloatArray(argument);
1568                        coerced = true;
1569                        continue;
1570                    }
1571                    if (paramElem == double.class &&
1572                            argument.getClass().getName().equals("[Ljava.lang.Double;") &&
1573                            argument.getClass().getName().equals("[Ljava.lang.BigDecimal;") &&
1574                            argument.getClass().getName().equals("[Ljava.lang.Float;")) {
1575                        ans[i] = InvokerHelper.convertToDoubleArray(argument);
1576                        coerced = true;
1577                        continue;
1578                    }
1579                }
1580            }
1581        }
1582        return coerced ? ans : null;
1583    }
1584
1585    protected Object JavaDoc doConstructorInvoke(Constructor JavaDoc constructor, Object JavaDoc[] argumentArray) {
1586        //System.out.println("Evaluating constructor: " + constructor + " with
1587
// arguments: " + InvokerHelper.toString(argumentArray));
1588
//System.out.println(this.theClass);
1589

1590        try {
1591            // the following patch was provided by Mori Kouhei to fix JIRA 435
1592
/* but it opens the ctor up to everyone, so it is no longer private!
1593            final Constructor ctor = constructor;
1594            AccessController.doPrivileged(new PrivilegedAction() {
1595                public Object run() {
1596                    ctor.setAccessible(ctor.getDeclaringClass().equals(theClass));
1597                    return null;
1598                }
1599            });
1600            */

1601            // end of patch
1602

1603            return constructor.newInstance(argumentArray);
1604        }
1605        catch (InvocationTargetException JavaDoc e) {
1606            /*Throwable t = e.getTargetException();
1607            if (t instanceof Error) {
1608                Error error = (Error) t;
1609                throw error;
1610            }
1611            if (t instanceof RuntimeException) {
1612                RuntimeException runtimeEx = (RuntimeException) t;
1613                throw runtimeEx;
1614            }*/

1615            throw new InvokerInvocationException(e);
1616        }
1617        catch (IllegalArgumentException JavaDoc e) {
1618            if (coerceGStrings(argumentArray)) {
1619                try {
1620                    return constructor.newInstance(argumentArray);
1621                }
1622                catch (Exception JavaDoc e2) {
1623                    // allow fall through
1624
}
1625            }
1626            throw new GroovyRuntimeException(
1627                "failed to invoke constructor: "
1628                    + constructor
1629                    + " with arguments: "
1630                    + InvokerHelper.toString(argumentArray)
1631                    + " reason: "
1632                    + e);
1633        }
1634        catch (IllegalAccessException JavaDoc e) {
1635            throw new GroovyRuntimeException(
1636                "could not access constructor: "
1637                    + constructor
1638                    + " with arguments: "
1639                    + InvokerHelper.toString(argumentArray)
1640                    + " reason: "
1641                    + e);
1642        }
1643        catch (Exception JavaDoc e) {
1644            throw new GroovyRuntimeException(
1645                "failed to invoke constructor: "
1646                    + constructor
1647                    + " with arguments: "
1648                    + InvokerHelper.toString(argumentArray)
1649                    + " reason: "
1650                    + e,
1651                    e);
1652        }
1653    }
1654
1655    /**
1656     * Chooses the correct method to use from a list of methods which match by
1657     * name.
1658     *
1659     * @param methods
1660     * the possible methods to choose from
1661     * @param arguments
1662     * the original argument to the method
1663     * @return
1664     */

1665    protected Object JavaDoc chooseMethod(String JavaDoc methodName, List methods, Class JavaDoc[] arguments, boolean coerce) {
1666        int methodCount = methods.size();
1667        if (methodCount <= 0) {
1668            return null;
1669        }
1670        else if (methodCount == 1) {
1671            Object JavaDoc method = methods.get(0);
1672            if (isValidMethod(method, arguments, coerce)) {
1673                return method;
1674            }
1675            return null;
1676        }
1677        Object JavaDoc answer = null;
1678        if (arguments == null || arguments.length == 0) {
1679            answer = chooseEmptyMethodParams(methods);
1680        }
1681        else if (arguments.length == 1 && arguments[0] == null) {
1682            answer = chooseMostGeneralMethodWith1NullParam(methods);
1683        }
1684        else {
1685            List matchingMethods = new ArrayList();
1686
1687            for (Iterator iter = methods.iterator(); iter.hasNext();) {
1688                Object JavaDoc method = iter.next();
1689                Class JavaDoc[] paramTypes;
1690
1691                // making this false helps find matches
1692
if (isValidMethod(method, arguments, coerce)) {
1693                    matchingMethods.add(method);
1694                }
1695            }
1696            if (matchingMethods.isEmpty()) {
1697                return null;
1698            }
1699            else if (matchingMethods.size() == 1) {
1700                return matchingMethods.get(0);
1701            }
1702            return chooseMostSpecificParams(methodName, matchingMethods, arguments);
1703
1704        }
1705        if (answer != null) {
1706            return answer;
1707        }
1708        throw new GroovyRuntimeException(
1709            "Could not find which method to invoke from this list: "
1710                + methods
1711                + " for arguments: "
1712                + InvokerHelper.toString(arguments));
1713    }
1714
1715    protected boolean isValidMethod(Object JavaDoc method, Class JavaDoc[] arguments, boolean includeCoerce) {
1716        Class JavaDoc[] paramTypes = getParameterTypes(method);
1717        return isValidMethod(paramTypes, arguments, includeCoerce);
1718    }
1719
1720    public static boolean isValidMethod(Class JavaDoc[] paramTypes, Class JavaDoc[] arguments, boolean includeCoerce) {
1721        if (arguments == null) {
1722            return true;
1723        }
1724        int size = arguments.length;
1725        boolean validMethod = false;
1726        if (paramTypes.length == size) {
1727            // lets check the parameter types match
1728
validMethod = true;
1729            for (int i = 0; i < size; i++) {
1730                if (!isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) {
1731                    validMethod = false;
1732                }
1733            }
1734        }
1735        else {
1736            if (paramTypes.length == 1 && size == 0) {
1737                return true;
1738            }
1739        }
1740        return validMethod;
1741    }
1742
1743    private boolean isSuperclass(Class JavaDoc claszz, Class JavaDoc superclass) {
1744        while (claszz!=null) {
1745            if (claszz==superclass) return true;
1746            claszz = claszz.getSuperclass();
1747        }
1748        return false;
1749    }
1750    
1751    private Class JavaDoc[] wrap(Class JavaDoc[] classes) {
1752        Class JavaDoc[] wrappedArguments = new Class JavaDoc[classes.length];
1753        for (int i = 0; i < wrappedArguments.length; i++) {
1754            Class JavaDoc c = classes[i];
1755            if (c==null) continue;
1756            if (c.isPrimitive()) {
1757                if (c==Integer.TYPE) {
1758                    c=Integer JavaDoc.class;
1759                } else if (c==Byte.TYPE) {
1760                    c=Byte JavaDoc.class;
1761                } else if (c==Long.TYPE) {
1762                    c=Long JavaDoc.class;
1763                } else if (c==Double.TYPE) {
1764                    c=Double JavaDoc.class;
1765                } else if (c==Float.TYPE) {
1766                    c=Float JavaDoc.class;
1767                }
1768            } else if (isSuperclass(c,GString.class)) {
1769                c = String JavaDoc.class;
1770            }
1771            wrappedArguments[i]=c;
1772        }
1773        return wrappedArguments;
1774    }
1775    
1776    protected Object JavaDoc chooseMostSpecificParams(String JavaDoc name, List matchingMethods, Class JavaDoc[] arguments) {
1777        
1778        Class JavaDoc[] wrappedArguments = wrap(arguments);
1779        LinkedList directMatches = new LinkedList();
1780        // test for a method with equal classes (natives are wrapped
1781
for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1782            Object JavaDoc method = iter.next();
1783            Class JavaDoc[] paramTypes = wrap(getParameterTypes(method));
1784            if (Arrays.equals(wrappedArguments, paramTypes)) directMatches.add(method);
1785        }
1786        if (directMatches.size()>0) {
1787            matchingMethods = directMatches;
1788        }
1789        if (directMatches.size()>1) {
1790            matchingMethods = directMatches;
1791            // we have more then one possible match for wrapped natives
1792
// so next test without using wrapping
1793
directMatches = new LinkedList();
1794            for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1795                Object JavaDoc method = iter.next();
1796                Class JavaDoc[] paramTypes = getParameterTypes(method);
1797                if (Arrays.equals(arguments, paramTypes)) directMatches.add(method);
1798            }
1799            if (directMatches.size()==1) matchingMethods = directMatches;
1800        }
1801        
1802        // filter out cases where we don't have a superclass
1803
List superclassMatches = new ArrayList(matchingMethods);
1804        for (Iterator iter = superclassMatches.iterator(); iter.hasNext(); ) {
1805            Object JavaDoc method = iter.next();
1806            Class JavaDoc[] paramTypes = getParameterTypes(method);
1807            for (int i=0; i<paramTypes.length; i++) {
1808                if (!isSuperclass(arguments[i],paramTypes[i])) {
1809                    iter.remove();
1810                    break; //return from the inner for
1811
}
1812            }
1813        }
1814        if (superclassMatches.size()!=0) {
1815            //if not all methods are filtered out use the filtered methods
1816
matchingMethods = superclassMatches;
1817        }
1818        
1819        Object JavaDoc answer = null;
1820        int size = arguments.length;
1821        Class JavaDoc[] mostSpecificTypes = null;
1822        for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1823            Object JavaDoc method = iter.next();
1824            Class JavaDoc[] paramTypes = getParameterTypes(method);
1825            if (answer == null) {
1826                answer = method;
1827                mostSpecificTypes = paramTypes;
1828            }
1829            else {
1830                boolean useThisMethod = false;
1831                for (int i = 0; i < size; i++) {
1832                    Class JavaDoc mostSpecificType = mostSpecificTypes[i];
1833                    Class JavaDoc type = paramTypes[i];
1834
1835                    if (!isAssignableFrom(mostSpecificType, type)) {
1836                        useThisMethod = true;
1837                        break;
1838                    }
1839                }
1840                if (useThisMethod) {
1841
1842                    if (size > 1) {
1843                        checkForInvalidOverloading(name, mostSpecificTypes, paramTypes);
1844                    }
1845
1846                    answer = method;
1847                    mostSpecificTypes = paramTypes;
1848                }
1849            }
1850        }
1851        return answer;
1852    }
1853
1854    /**
1855     * Checks that one of the parameter types is a superset of the other and
1856     * that the two lists of types don't conflict. e.g. foo(String, Object) and
1857     * foo(Object, String) would conflict if called with foo("a", "b").
1858     *
1859     * Note that this method is only called with 2 possible signatures. i.e.
1860     * possible invalid combinations will already have been filtered out. So if
1861     * there were methods foo(String, Object) and foo(Object, String) then one
1862     * of these would be already filtered out if foo was called as foo(12, "a")
1863     */

1864    protected void checkForInvalidOverloading(String JavaDoc name, Class JavaDoc[] baseTypes, Class JavaDoc[] derivedTypes) {
1865        for (int i = 0, size = baseTypes.length; i < size; i++) {
1866            Class JavaDoc baseType = baseTypes[i];
1867            Class JavaDoc derivedType = derivedTypes[i];
1868            if (!isAssignableFrom(derivedType, baseType)) {
1869                throw new GroovyRuntimeException(
1870                    "Ambiguous method overloading for method: "
1871                        + name
1872                        + ". Cannot resolve which method to invoke due to overlapping prototypes between: "
1873                        + InvokerHelper.toString(baseTypes)
1874                        + " and: "
1875                        + InvokerHelper.toString(derivedTypes));
1876            }
1877        }
1878    }
1879
1880    protected Class JavaDoc[] getParameterTypes(Object JavaDoc methodOrConstructor) {
1881        if (methodOrConstructor instanceof MetaMethod) {
1882            MetaMethod method = (MetaMethod) methodOrConstructor;
1883            return method.getParameterTypes();
1884        }
1885        if (methodOrConstructor instanceof Method JavaDoc) {
1886            Method JavaDoc method = (Method JavaDoc) methodOrConstructor;
1887            return method.getParameterTypes();
1888        }
1889        if (methodOrConstructor instanceof Constructor JavaDoc) {
1890            Constructor JavaDoc constructor = (Constructor JavaDoc) methodOrConstructor;
1891            return constructor.getParameterTypes();
1892        }
1893        throw new IllegalArgumentException JavaDoc("Must be a Method or Constructor");
1894    }
1895
1896    /**
1897     * @return the method with 1 parameter which takes the most general type of
1898     * object (e.g. Object) ignoring primitve types
1899     */

1900    protected Object JavaDoc chooseMostGeneralMethodWith1NullParam(List methods) {
1901        // lets look for methods with 1 argument which matches the type of the
1902
// arguments
1903
Class JavaDoc closestClass = null;
1904        Object JavaDoc answer = null;
1905
1906        for (Iterator iter = methods.iterator(); iter.hasNext();) {
1907            Object JavaDoc method = iter.next();
1908            Class JavaDoc[] paramTypes = getParameterTypes(method);
1909            int paramLength = paramTypes.length;
1910            if (paramLength == 1) {
1911                Class JavaDoc theType = paramTypes[0];
1912                if (theType.isPrimitive()) continue;
1913                if (closestClass == null || isAssignableFrom(closestClass, theType)) {
1914                    closestClass = theType;
1915                    answer = method;
1916                }
1917            }
1918        }
1919        return answer;
1920    }
1921
1922    /**
1923     * @return the method with 1 parameter which takes the most general type of
1924     * object (e.g. Object)
1925     */

1926    protected Object JavaDoc chooseEmptyMethodParams(List methods) {
1927        for (Iterator iter = methods.iterator(); iter.hasNext();) {
1928            Object JavaDoc method = iter.next();
1929            Class JavaDoc[] paramTypes = getParameterTypes(method);
1930            int paramLength = paramTypes.length;
1931            if (paramLength == 0) {
1932                return method;
1933            }
1934        }
1935        return null;
1936    }
1937
1938    protected static boolean isCompatibleInstance(Class JavaDoc type, Object JavaDoc value, boolean includeCoerce) {
1939        boolean answer = value == null || type.isInstance(value);
1940        if (!answer) {
1941            if (type.isPrimitive()) {
1942                if (type == int.class) {
1943                    return value instanceof Integer JavaDoc;
1944                }
1945                else if (type == double.class) {
1946                    return value instanceof Double JavaDoc || value instanceof Float JavaDoc || value instanceof Integer JavaDoc || value instanceof BigDecimal JavaDoc;
1947                }
1948                else if (type == boolean.class) {
1949                    return value instanceof Boolean JavaDoc;
1950                }
1951                else if (type == long.class) {
1952                    return value instanceof Long JavaDoc || value instanceof Integer JavaDoc;
1953                }
1954                else if (type == float.class) {
1955                    return value instanceof Float JavaDoc || value instanceof Integer JavaDoc;
1956                }
1957                else if (type == char.class) {
1958                    return value instanceof Character JavaDoc;
1959                }
1960                else if (type == byte.class) {
1961                    return value instanceof Byte JavaDoc;
1962                }
1963                else if (type == short.class) {
1964                    return value instanceof Short JavaDoc;
1965                }
1966            }
1967            else if(type.isArray() && value.getClass().isArray()) {
1968                return isCompatibleClass(type.getComponentType(), value.getClass().getComponentType(), false);
1969            }
1970            else if (includeCoerce) {
1971                if (type == String JavaDoc.class && value instanceof GString) {
1972                    return true;
1973                }
1974                else if (value instanceof Number JavaDoc) {
1975                    // lets allow numbers to be coerced downwards?
1976
return Number JavaDoc.class.isAssignableFrom(type);
1977                }
1978            }
1979        }
1980        return answer;
1981    }
1982    protected static boolean isCompatibleClass(Class JavaDoc type, Class JavaDoc value, boolean includeCoerce) {
1983        boolean answer = value == null || type.isAssignableFrom(value); // this might have taken care of primitive types, rendering part of the following code unnecessary
1984
if (!answer) {
1985            if (type.isPrimitive()) {
1986                if (type == int.class) {
1987                    return value == Integer JavaDoc.class;// || value == BigDecimal.class; //br added BigDecimal
1988
}
1989                else if (type == double.class) {
1990                    return value == Double JavaDoc.class || value == Float JavaDoc.class || value == Integer JavaDoc.class || value == BigDecimal JavaDoc.class;
1991                }
1992                else if (type == boolean.class) {
1993                    return value == Boolean JavaDoc.class;
1994                }
1995                else if (type == long.class) {
1996                    return value == Long JavaDoc.class || value == Integer JavaDoc.class; // || value == BigDecimal.class;//br added BigDecimal
1997
}
1998                else if (type == float.class) {
1999                    return value == Float JavaDoc.class || value == Integer JavaDoc.class; // || value == BigDecimal.class;//br added BigDecimal
2000
}
2001                else if (type == char.class) {
2002                    return value == Character JavaDoc.class;
2003                }
2004                else if (type == byte.class) {
2005                    return value == Byte JavaDoc.class;
2006                }
2007                else if (type == short.class) {
2008                    return value == Short JavaDoc.class;
2009                }
2010            }
2011            else if(type.isArray() && value.isArray()) {
2012                return isCompatibleClass(type.getComponentType(), value.getComponentType(), false);
2013            }
2014            else if (includeCoerce) {
2015//if (type == String.class && value == GString.class) {
2016
if (type == String JavaDoc.class && GString.class.isAssignableFrom(value)) {
2017                    return true;
2018                }
2019                else if (value == Number JavaDoc.class) {
2020                    // lets allow numbers to be coerced downwards?
2021
return Number JavaDoc.class.isAssignableFrom(type);
2022                }
2023            }
2024        }
2025        return answer;
2026    }
2027
2028    protected boolean isAssignableFrom(Class JavaDoc mostSpecificType, Class JavaDoc type) {
2029        // let's handle primitives
2030
if (mostSpecificType.isPrimitive() && type.isPrimitive()) {
2031            if (mostSpecificType == type) {
2032                return true;
2033            }
2034            else { // note: there is not coercion for boolean and char. Range matters, precision doesn't
2035
if (type == int.class) {
2036                    return
2037                            mostSpecificType == int.class
2038                            || mostSpecificType == short.class
2039                            || mostSpecificType == byte.class;
2040                }
2041                else if (type == double.class) {
2042                    return
2043                            mostSpecificType == double.class
2044                            || mostSpecificType == int.class
2045                            || mostSpecificType == long.class
2046                            || mostSpecificType == short.class
2047                            || mostSpecificType == byte.class
2048                            || mostSpecificType == float.class;
2049                }
2050                else if (type == long.class) {
2051                    return
2052                            mostSpecificType == long.class
2053                            || mostSpecificType == int.class
2054                            || mostSpecificType == short.class
2055                            || mostSpecificType == byte.class;
2056                }
2057                else if (type == float.class) {
2058                    return
2059                            mostSpecificType == float.class
2060                            || mostSpecificType == int.class
2061                            || mostSpecificType == long.class
2062                            || mostSpecificType == short.class
2063                            || mostSpecificType == byte.class;
2064                }
2065                else if (type == short.class) {
2066                    return
2067                            mostSpecificType == short.class
2068                            || mostSpecificType == byte.class;
2069                }
2070                else {
2071                    return false;
2072                }
2073            }
2074        }
2075
2076        boolean answer = type.isAssignableFrom(mostSpecificType);
2077        if (!answer) {
2078            answer = autoboxType(type).isAssignableFrom(autoboxType(mostSpecificType));
2079        }
2080        return answer;
2081    }
2082
2083    private Class JavaDoc autoboxType(Class JavaDoc type) {
2084        if (type.isPrimitive()) {
2085            if (type == int.class) {
2086                return Integer JavaDoc.class;
2087            }
2088            else if (type == double.class) {
2089                return Double JavaDoc.class;
2090            }
2091            else if (type == long.class) {
2092                return Long JavaDoc.class;
2093            }
2094            else if (type == boolean.class) {
2095                return Boolean JavaDoc.class;
2096            }
2097            else if (type == float.class) {
2098                return Float JavaDoc.class;
2099            }
2100            else if (type == char.class) {
2101                return Character JavaDoc.class;
2102            }
2103            else if (type == byte.class) {
2104                return Byte JavaDoc.class;
2105            }
2106            else if (type == short.class) {
2107                return Short JavaDoc.class;
2108            }
2109        }
2110        return type;
2111    }
2112
2113    /**
2114     * Coerces any GString instances into Strings
2115     *
2116     * @return true if some coercion was done.
2117     */

2118    protected static boolean coerceGStrings(Object JavaDoc[] arguments) {
2119        boolean coerced = false;
2120        for (int i = 0, size = arguments.length; i < size; i++) {
2121            Object JavaDoc argument = arguments[i];
2122            if (argument instanceof GString) {
2123                arguments[i] = argument.toString();
2124                coerced = true;
2125            }
2126        }
2127        return coerced;
2128    }
2129
2130    protected boolean isGenericSetMethod(MetaMethod method) {
2131        return (method.getName().equals("set"))
2132            && method.getParameterTypes().length == 2;
2133    }
2134
2135    protected boolean isGenericGetMethod(MetaMethod method) {
2136        if (method.getName().equals("get")) {
2137            Class JavaDoc[] parameterTypes = method.getParameterTypes();
2138            return parameterTypes.length == 1 && parameterTypes[0] == String JavaDoc.class;
2139        }
2140        return false;
2141    }
2142
2143    private void registerMethods(boolean instanceMethods) {
2144        Method JavaDoc[] methods = theClass.getMethods();
2145        for (int i = 0; i < methods.length; i++) {
2146            Method JavaDoc method = methods[i];
2147            if (MethodHelper.isStatic(method)) {
2148                Class JavaDoc[] paramTypes = method.getParameterTypes();
2149                if (paramTypes.length > 0) {
2150                    Class JavaDoc owner = paramTypes[0];
2151                    if (instanceMethods) {
2152                        registry.lookup(owner).addNewInstanceMethod(method);
2153                    } else {
2154                        registry.lookup(owner).addNewStaticMethod(method);
2155                    }
2156                }
2157            }
2158        }
2159    }
2160
2161    protected void registerStaticMethods() {
2162        registerMethods(false);
2163    }
2164
2165    protected void registerInstanceMethods() {
2166        registerMethods(true);
2167    }
2168
2169    protected String JavaDoc capitalize(String JavaDoc property) {
2170        return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
2171    }
2172
2173    /**
2174     * Call this method when any mutation method is called, such as adding a new
2175     * method to this MetaClass so that any caching or bytecode generation can be
2176     * regenerated.
2177     */

2178    protected synchronized void onMethodChange() {
2179        reflector = null;
2180    }
2181    
2182    protected synchronized void checkInitialised() {
2183        if (!initialised) {
2184            initialised = true;
2185            addInheritedMethods();
2186        }
2187        if (reflector == null) {
2188            generateReflector();
2189        }
2190    }
2191
2192    protected MetaMethod createMetaMethod(final Method JavaDoc method) {
2193        if (registry.useAccessible()) {
2194            AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
2195                public Object JavaDoc run() {
2196                    method.setAccessible(true);
2197                    return null;
2198                }
2199            });
2200        }
2201        if (useReflection) {
2202            //log.warning("Creating reflection based dispatcher for: " + method);
2203
return new ReflectionMetaMethod(method);
2204        }
2205        MetaMethod answer = new MetaMethod(method);
2206        if (isValidReflectorMethod(answer)) {
2207            allMethods.add(answer);
2208            answer.setMethodIndex(allMethods.size());
2209        }
2210        else {
2211            //log.warning("Creating reflection based dispatcher for: " + method);
2212
answer = new ReflectionMetaMethod(method);
2213        }
2214        return answer;
2215    }
2216
2217    protected boolean isValidReflectorMethod(MetaMethod method) {
2218        // We cannot use a reflector if the method is private, protected, or package accessible only.
2219
if (!method.isPublic()) {
2220            return false;
2221        }
2222        Class JavaDoc declaringClass = method.getDeclaringClass();
2223        if (!Modifier.isPublic(declaringClass.getModifiers())) {
2224            // lets see if this method is implemented on an interface
2225
List list = getInterfaceMethods();
2226            for (Iterator iter = list.iterator(); iter.hasNext();) {
2227                MetaMethod aMethod = (MetaMethod) iter.next();
2228                if (method.isSame(aMethod)) {
2229                    method.setInterfaceClass(aMethod.getDeclaringClass());
2230                    return true;
2231                }
2232            }
2233            /** todo */
2234            //log.warning("Cannot invoke method on protected/private class which isn't visible on an interface so must use reflection instead: " + method);
2235
return false;
2236        }
2237        return true;
2238    }
2239
2240    protected void generateReflector() {
2241        reflector = loadReflector(allMethods);
2242        if (reflector == null) {
2243            throw new RuntimeException JavaDoc("Should have a reflector!");
2244        }
2245        // lets set the reflector on all the methods
2246
for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
2247            MetaMethod metaMethod = (MetaMethod) iter.next();
2248            //System.out.println("Setting reflector for method: " + metaMethod + " with index: " + metaMethod.getMethodIndex());
2249
metaMethod.setReflector(reflector);
2250        }
2251    }
2252
2253    protected Reflector loadReflector(List methods) {
2254        ReflectorGenerator generator = new ReflectorGenerator(methods);
2255        String JavaDoc className = theClass.getName();
2256        String JavaDoc packagePrefix = "gjdk.";
2257        /*
2258        if (className.startsWith("java.")) {
2259            packagePrefix = "gjdk.";
2260        }
2261        */

2262        String JavaDoc name = packagePrefix + className + "_GroovyReflector";
2263        if (theClass.isArray()) {
2264            String JavaDoc componentName = theClass.getComponentType().getName();
2265            /*
2266            if (componentName.startsWith("java.")) {
2267                packagePrefix = "gjdk.";
2268            }
2269            */

2270            name = packagePrefix + componentName + "_GroovyReflectorArray";
2271        }
2272        // lets see if its already loaded
2273
try {
2274            Class JavaDoc type = loadReflectorClass(name);
2275            return (Reflector) type.newInstance();
2276        }
2277        catch (AccessControlException JavaDoc ace) {
2278            //Don't ignore this exception type
2279
throw ace;
2280        }
2281        catch (Exception JavaDoc e) {
2282            // lets ignore, lets generate it && load it
2283
}
2284
2285        ClassWriter cw = new ClassWriter(true);
2286        generator.generate(cw, name);
2287
2288        byte[] bytecode = cw.toByteArray();
2289
2290        try {
2291            Class JavaDoc type = loadReflectorClass(name, bytecode);
2292            return (Reflector) type.newInstance();
2293        }
2294        catch (Exception JavaDoc e) {
2295            throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + e, e);
2296        }
2297    }
2298
2299    protected Class JavaDoc loadReflectorClass(final String JavaDoc name, final byte[] bytecode) throws ClassNotFoundException JavaDoc {
2300        ClassLoader JavaDoc loader = theClass.getClassLoader();
2301        if (loader instanceof GroovyClassLoader) {
2302            final GroovyClassLoader gloader = (GroovyClassLoader) loader;
2303            return (Class JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
2304                public Object JavaDoc run() {
2305                    return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
2306                }
2307            });
2308        }
2309        return registry.loadClass(loader, name, bytecode);
2310    }
2311
2312    protected Class JavaDoc loadReflectorClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
2313        ClassLoader JavaDoc loader = theClass.getClassLoader();
2314        if (loader instanceof GroovyClassLoader) {
2315            GroovyClassLoader gloader = (GroovyClassLoader) loader;
2316            return gloader.loadClass(name);
2317        }
2318        return registry.loadClass(loader, name);
2319    }
2320
2321    public List getMethods() {
2322        return allMethods;
2323    }
2324
2325    public List getMetaMethods() {
2326        return (List) ((ArrayList)newGroovyMethodsList).clone();
2327    }
2328
2329    protected synchronized List getInterfaceMethods() {
2330        if (interfaceMethods == null) {
2331            interfaceMethods = new ArrayList();
2332            Class JavaDoc type = theClass;
2333            while (type != null) {
2334                Class JavaDoc[] interfaces = type.getInterfaces();
2335                for (int i = 0; i < interfaces.length; i++) {
2336                    Class JavaDoc iface = interfaces[i];
2337                    Method JavaDoc[] methods = iface.getMethods();
2338                    addInterfaceMethods(interfaceMethods, methods);
2339                }
2340                type = type.getSuperclass();
2341            }
2342        }
2343        return interfaceMethods;
2344    }
2345
2346    private void addInterfaceMethods(List list, Method JavaDoc[] methods) {
2347        for (int i = 0; i < methods.length; i++) {
2348            list.add(createMetaMethod(methods[i]));
2349        }
2350    }
2351
2352    /**
2353     * param instance array to the type array
2354     * @param args
2355     * @return
2356     */

2357    Class JavaDoc[] convertToTypeArray(Object JavaDoc[] args) {
2358        if (args == null)
2359            return null;
2360        int s = args.length;
2361        Class JavaDoc[] ans = new Class JavaDoc[s];
2362        for (int i = 0; i < s; i++) {
2363            Object JavaDoc o = args[i];
2364            if (o != null) {
2365                ans[i] = o.getClass();
2366            } else {
2367                ans[i] = null;
2368            }
2369        }
2370        return ans;
2371    }
2372
2373}
2374
Popular Tags