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 s