001    /*
002    $Id: MetaClassImpl.java 4611 2006-12-23 11:17:12Z blackdrag $
003    
004    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006    Redistribution and use of this software and associated documentation
007    ("Software"), with or without modification, are permitted provided
008    that the following conditions are met:
009    
010    1. Redistributions of source code must retain copyright
011       statements and notices.  Redistributions must also contain a
012       copy of this document.
013    
014    2. Redistributions in binary form must reproduce the
015       above copyright notice, this list of conditions and the
016       following disclaimer in the documentation and/or other
017       materials provided with the distribution.
018    
019    3. The name "groovy" must not be used to endorse or promote
020       products derived from this Software without prior written
021       permission of The Codehaus.  For written permission,
022       please contact info@codehaus.org.
023    
024    4. Products derived from this Software may not be called "groovy"
025       nor may "groovy" appear in their names without prior written
026       permission of The Codehaus. "groovy" is a registered
027       trademark of The Codehaus.
028    
029    5. Due credit should be given to The Codehaus -
030       http://groovy.codehaus.org/
031    
032    THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033    ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036    THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043    OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045    */
046    package groovy.lang;
047    
048    import java.beans.BeanInfo;
049    import java.beans.EventSetDescriptor;
050    import java.beans.IntrospectionException;
051    import java.beans.Introspector;
052    import java.beans.PropertyDescriptor;
053    import java.lang.reflect.Constructor;
054    import java.lang.reflect.Field;
055    import java.lang.reflect.Method;
056    import java.lang.reflect.Modifier;
057    import java.net.URL;
058    import java.security.AccessController;
059    import java.security.PrivilegedAction;
060    import java.security.PrivilegedActionException;
061    import java.security.PrivilegedExceptionAction;
062    import java.util.ArrayList;
063    import java.util.Arrays;
064    import java.util.Collection;
065    import java.util.Collections;
066    import java.util.Comparator;
067    import java.util.HashMap;
068    import java.util.HashSet;
069    import java.util.Iterator;
070    import java.util.LinkedList;
071    import java.util.List;
072    import java.util.Map;
073    import java.util.Set;
074    import java.util.logging.Level;
075    
076    import org.codehaus.groovy.GroovyBugError;
077    import org.codehaus.groovy.ast.ClassNode;
078    import org.codehaus.groovy.classgen.BytecodeHelper;
079    import org.codehaus.groovy.control.CompilationUnit;
080    import org.codehaus.groovy.control.Phases;
081    import org.codehaus.groovy.runtime.CurriedClosure;
082    import org.codehaus.groovy.runtime.DefaultGroovyMethods;
083    import org.codehaus.groovy.runtime.DefaultMethodKey;
084    import org.codehaus.groovy.runtime.GroovyCategorySupport;
085    import org.codehaus.groovy.runtime.InvokerHelper;
086    import org.codehaus.groovy.runtime.MetaClassHelper;
087    import org.codehaus.groovy.runtime.MethodClosure;
088    import org.codehaus.groovy.runtime.MethodKey;
089    import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
090    import org.codehaus.groovy.runtime.NewStaticMetaMethod;
091    import org.codehaus.groovy.runtime.ReflectionMetaMethod;
092    import org.codehaus.groovy.runtime.Reflector;
093    import org.codehaus.groovy.runtime.TransformMetaMethod;
094    import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
095    import org.codehaus.groovy.runtime.wrappers.Wrapper;
096    import org.objectweb.asm.ClassVisitor;
097    
098    /**
099    * Allows methods to be dynamically added to existing classes at runtime
100    *
101    * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
102    * @author Guillaume Laforge
103    * @author Jochen Theodorou
104    * @version $Revision: 4611 $
105    * @see groovy.lang.MetaClass
106    */
107    public class MetaClassImpl extends MetaClass {
108    
109       protected MetaClassRegistry registry;
110       private ClassNode classNode;
111       private Map classMethodIndex = new HashMap();
112       private Map classMethodIndexForSuper;
113       private Map classStaticMethodIndex = new HashMap();
114       private Map classPropertyIndex = new HashMap();
115       private Map classPropertyIndexForSuper = new HashMap();
116       private Map staticPropertyIndex = new HashMap();
117       private Map listeners = new HashMap();
118       private Map methodCache = Collections.synchronizedMap(new HashMap());
119       private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
120       private MetaMethod genericGetMethod;
121       private MetaMethod genericSetMethod;
122       private List constructors;
123       private List allMethods = new ArrayList();
124       private List interfaceMethods;
125       private Reflector reflector;
126       private boolean initialized;
127       // we only need one of these that can be reused over and over.
128       private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
129       private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new Class[]{},null,0);
130       private static final Object[] EMPTY_ARGUMENTS = {};
131       private List newGroovyMethodsList = new LinkedList();
132       
133       public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
134           super(theClass);
135           this.registry = registry;
136    
137           constructors = (List) AccessController.doPrivileged(new  PrivilegedAction() {
138                   public Object run() {
139                       return Arrays.asList (theClass.getDeclaredConstructors());
140                   }
141               });
142       }
143    
144       private void fillMethodIndex() {
145           LinkedList superClasses = getSuperClasses();
146           // let's add all the base class methods
147           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
148               Class c = (Class) iter.next();
149               addMethods(c);
150           }
151    
152           Set interfaces = new HashSet();
153           makeInterfaceSet(theClass,interfaces); 
154    
155           inheritMethods(superClasses,classMethodIndex);
156           inheritInterfaceMethods(interfaces);
157           copyClassMethodIndexForSuper();
158           
159           connectMultimethods(superClasses);
160           populateInterfaces(interfaces);
161           removeMultimethodsOverloadedWithPrivateMethods();
162           
163           replaceWithMOPCalls();
164       }
165       
166       private LinkedList getSuperClasses() {
167           LinkedList superClasses = new LinkedList();
168           for (Class c = theClass; c!= null; c = c.getSuperclass()) {
169               superClasses.addFirst(c);
170           }
171           if (theClass.isArray() && theClass!=Object[].class && !theClass.getComponentType().isPrimitive()) {
172               superClasses.addFirst(Object[].class);
173           }
174           return superClasses;
175       }
176    
177       private void removeMultimethodsOverloadedWithPrivateMethods() {
178           Map privates = new HashMap();
179           MethodIndexAction mia = new MethodIndexAction() {
180               public List methodNameAction(Class clazz, String methodName, List methods) {
181                  boolean hasPrivate=false;
182                  for (Iterator iter = methods.iterator(); iter.hasNext();) {
183                      MetaMethod method = (MetaMethod) iter.next();
184                      if (method.isPrivate() && clazz == method.getDeclaringClass()) {
185                          hasPrivate = true;
186                          break;
187                      }
188                  }
189                  if (!hasPrivate) return null;
190                  // We have private methods for that name, so remove the
191                  // multimethods. That is the same as in our index for 
192                  // super, so just copy the list from there. It is not
193                  // possible to use a pointer here, because the methods
194                  // in the index for super are replaced later by MOP 
195                  // methods like super$5$foo              
196                  methods.clear();
197                  methods.addAll((Collection) ((Map) classMethodIndexForSuper.get(clazz)).get(methodName));
198                  return methods;
199               }
200               public boolean replaceMethodList() {return false;}
201           };
202           mia.iterate(classMethodIndex);
203       }
204       
205       
206       private void replaceWithMOPCalls() {
207           // no MOP methods if not a child of GroovyObject
208           if (!GroovyObject.class.isAssignableFrom(theClass)) return;
209           
210           final Map mainClassMethodIndex = (Map) classMethodIndex.get(theClass);
211           class MOPIter extends MethodIndexAction {
212               boolean useThis;
213               public boolean skipClass(Class clazz) {
214                   return !useThis && clazz==theClass;
215               }
216               public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {
217                   String mopName = getMOPMethodName(method.getDeclaringClass(), methodName,useThis);
218                   List matches = (List) mainClassMethodIndex.get(mopName);
219                   if (matches==null) {
220                       newList.add(method);
221                       return;
222                   }
223                   matches = new ArrayList(matches);
224                   MetaMethod matchingMethod = removeMatchingMethod(matches,method);
225                   if (matchingMethod==null) {
226                       newList.add(method);
227                       return;
228                   } else {
229                       newList.add(matchingMethod);
230                   }
231               }
232           }
233           MOPIter iter = new MOPIter();
234           
235           // replace all calls for super with the correct MOP method
236           iter.useThis = false;
237           iter.iterate(classMethodIndexForSuper);
238           // replace all calls for this with the correct MOP method
239           iter.useThis = true;
240           iter.iterate(classMethodIndex);
241       }
242       
243       private String getMOPMethodName(Class declaringClass, String name, boolean useThis) {
244           int distance = 0;
245           for (;declaringClass!=null; declaringClass=declaringClass.getSuperclass()) {
246               distance++;
247           }
248           return (useThis?"this":"super")+"$"+distance+"$"+name;
249       }
250       
251       private void copyClassMethodIndexForSuper() {
252           classMethodIndexForSuper = new HashMap(classMethodIndex.size());
253           for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) {
254               Map.Entry cmiEntry = (Map.Entry) iter.next();
255               Map methodIndex = (Map) cmiEntry.getValue();
256               Map copy = new HashMap (methodIndex.size());
257               for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
258                   Map.Entry mEntry = (Map.Entry) iterator.next();
259                   copy.put(mEntry.getKey(), new ArrayList((List) mEntry.getValue()));
260               }
261               classMethodIndexForSuper.put(cmiEntry.getKey(),copy);
262           } 
263       }
264       
265       private void inheritInterfaceMethods(Set interfaces) {
266           // add methods declared by DGM for interfaces
267           List methods = registry.getInstanceMethods();
268           for (Iterator iter = methods.iterator(); iter.hasNext();) {
269               Method element = (Method) iter.next();
270               Class dgmClass = element.getParameterTypes()[0]; 
271               if (!interfaces.contains(dgmClass)) continue;
272               NewInstanceMetaMethod method = new NewInstanceMetaMethod(createMetaMethod(element));
273               if (! newGroovyMethodsList.contains(method)){
274                   newGroovyMethodsList.add(method);
275               }
276               Map methodIndex = (Map) classMethodIndex.get(theClass);
277               List list = (List) methodIndex.get(method.getName());
278               if (list == null) {
279                   list = new ArrayList();
280                   methodIndex.put(method.getName(), list);
281                   list.add(method);
282               } else {
283                   addMethodToList(list,method);
284               }
285           }
286           methods = registry.getStaticMethods();
287           for (Iterator iter = methods.iterator(); iter.hasNext();) {
288               Method element = (Method) iter.next();
289               Class dgmClass = element.getParameterTypes()[0]; 
290               if (!interfaces.contains(dgmClass)) continue;
291               addNewStaticMethod(element);
292           }
293       }
294       
295       private void populateInterfaces(Set interfaces){
296           Map currentIndex = (Map) classMethodIndex.get(theClass);
297           Map index = new HashMap();
298           copyNonPrivateMethods(currentIndex,index);
299           for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
300               Class iClass = (Class) iter.next();
301               Map methodIndex = (Map) classMethodIndex.get(iClass);
302               if (methodIndex==null || methodIndex.size()==0) {
303                   classMethodIndex.put(iClass,index);
304                   continue;
305               }
306               copyNonPrivateMethods(currentIndex,methodIndex);
307           }
308       }
309       
310       private static void makeInterfaceSet(Class c, Set s) {
311           if (c==null) return;
312           Class[] interfaces = c.getInterfaces();
313           for (int i = 0; i < interfaces.length; i++) {
314               if (!s.contains(interfaces[i])) {
315                   s.add(interfaces[i]);
316                   makeInterfaceSet(interfaces[i],s);
317               }
318           }
319           makeInterfaceSet(c.getSuperclass(),s);
320       }
321       
322       private void copyNonPrivateMethods(Map from, Map to) {
323           for (Iterator iterator = from.entrySet().iterator(); iterator.hasNext();) {
324               Map.Entry element = (Map.Entry) iterator.next();
325               List oldList = (List) element.getValue();
326               List newList = (List) to.get(element.getKey());
327               if (newList==null) {
328                   to.put(element.getKey(),new ArrayList(oldList));
329               } else {
330                   addNonPrivateMethods(newList,oldList);
331               }
332           }
333       }
334       
335       private void connectMultimethods(List superClasses){
336           superClasses = DefaultGroovyMethods.reverse(superClasses);
337           Map last = null;
338           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
339               Class c = (Class) iter.next();
340               Map methodIndex = (Map) classMethodIndex.get(c);
341               if (methodIndex==last) continue;
342               if (last!=null) copyNonPrivateMethods(last,methodIndex);
343               last = methodIndex;
344           }
345       }
346       
347       private void inheritMethods(Collection superClasses, Map classMethodIndex){
348           Map last = null;
349           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
350               Class c = (Class) iter.next();
351               Map methodIndex = (Map) classMethodIndex.get(c);
352               if (last!=null) {
353                   if (methodIndex.size()==0) {
354                       classMethodIndex.put(c,last);
355                       continue;
356                   }
357                   copyNonPrivateMethods(last,methodIndex);
358               }
359               last = methodIndex;
360           }
361       }
362    
363       private void addNonPrivateMethods(List newList, List oldList) {
364           for (Iterator iter = oldList.iterator(); iter.hasNext();) {
365               MetaMethod element = (MetaMethod) iter.next();
366               if (element.isPrivate()) continue;
367               addMethodToList(newList,element);
368           }
369       }
370    
371    /**
372        * @return all the normal instance methods avaiable on this class for the
373        *         given name
374        */
375       private List getMethods(Class sender, String name, boolean isCallToSuper) {
376           Map methodIndex;
377           if (isCallToSuper) {
378               methodIndex = (Map) classMethodIndexForSuper.get(sender);
379           } else {
380               methodIndex = (Map) classMethodIndex.get(sender);
381           }   
382           List answer;
383           if (methodIndex!=null) {
384               answer = (List) methodIndex.get(name);
385               if (answer == null) answer = Collections.EMPTY_LIST;
386           } else {
387               answer = Collections.EMPTY_LIST;
388           }
389           
390           if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) {
391               List used = GroovyCategorySupport.getCategoryMethods(sender, name);
392               if (used != null) {
393                   answer = new ArrayList(answer);
394                   for (Iterator iter = used.iterator(); iter.hasNext();) {
395                       MetaMethod element = (MetaMethod) iter.next();
396                       removeMatchingMethod(answer,element);
397                   }
398                   answer.addAll(used);
399               }
400           }
401           return answer;
402       }
403    
404       /**
405        * @return all the normal static methods avaiable on this class for the
406        *         given name
407        */
408       private List getStaticMethods(Class sender, String name) {
409           Map methodIndex = (Map) classStaticMethodIndex.get(sender);
410           if (methodIndex == null) return Collections.EMPTY_LIST;
411           List answer = (List) methodIndex.get(name);
412           if (answer == null) return Collections.EMPTY_LIST;
413           return answer;
414       }
415    
416       public void addNewInstanceMethod(Method method) {
417           NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
418           if (! newGroovyMethodsList.contains(newMethod)){
419               newGroovyMethodsList.add(newMethod);
420               addMetaMethod(newMethod);
421           }
422       }
423    
424       public void addNewStaticMethod(Method method) {
425           NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
426           if (! newGroovyMethodsList.contains(newMethod)){
427               newGroovyMethodsList.add(newMethod);
428               addMetaMethod(newMethod);
429           }
430       }
431    
432       private void unwrap(Object[] arguments) {
433           //
434           // Temp code to ignore wrapped parameters
435           // The New MOP will deal with these properly
436           //
437           for (int i = 0; i != arguments.length; i++) {
438            if (arguments[i] instanceof Wrapper) {
439              arguments[i] = ((Wrapper)arguments[i]).unwrap();
440            }
441           }       
442       }
443       
444       
445       /**
446        * Invokes the given method on the object.
447        * @deprecated
448        */
449       public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
450           return invokeMethod(theClass,object,methodName,originalArguments,false,false);
451       }
452       
453       
454       /**
455        * Invokes the given method on the object.
456        *
457        */
458       public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
459           checkInitalised();
460           if (object == null) {
461               throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
462           }              
463           if (log.isLoggable(Level.FINER)){
464               MetaClassHelper.logMethodCall(object, methodName, originalArguments);
465           }
466           Object[] arguments = originalArguments;
467           if (arguments==null) arguments = EMPTY_ARGUMENTS;
468           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
469           unwrap(arguments);
470           
471           MetaMethod method = retrieveMethod(sender, methodName, argClasses, isCallToSuper);
472           
473           if (method==null && arguments.length==1 && arguments[0] instanceof List) {
474               Object[] newArguments = ((List) arguments[0]).toArray();
475               Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
476               method = retrieveMethod(sender, methodName, newArgClasses, isCallToSuper);
477               if (method!=null) {
478                   MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses);
479                   method = new TransformMetaMethod(method) {
480                       public Object invoke(Object object, Object[] arguments) {
481                           Object firstArgument = arguments[0];
482                           List list = (List) firstArgument;
483                           arguments = list.toArray();
484                           return super.invoke(object, arguments);
485                       }
486                   };
487                   cacheInstanceMethod(methodKey, method);
488                   return invokeMethod(sender,object,methodName, originalArguments, isCallToSuper, fromInsideClass);
489               }
490           }
491    
492           boolean isClosure = object instanceof Closure;
493           if (isClosure) {
494               Closure closure = (Closure) object;
495               Object delegate = closure.getDelegate();
496               Object owner = closure.getOwner();
497               
498               
499               if ("call".equals(methodName) || "doCall".equals(methodName)) {
500                   if (object.getClass()==MethodClosure.class) {
501                       MethodClosure mc = (MethodClosure) object;
502                       methodName = mc.getMethod();
503                       Class ownerClass = owner.getClass();
504                       if (owner instanceof Class) ownerClass = (Class) owner;
505                       MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
506                       return ownerMetaClass.invokeMethod(ownerClass,owner,methodName,arguments,false,false);
507                   } else if (object.getClass()==CurriedClosure.class) {
508                       CurriedClosure cc = (CurriedClosure) object;
509                       // change the arguments for an uncurried call
510                       arguments = cc.getUncurriedArguments(arguments);
511                       Class ownerClass = owner.getClass();
512                       if (owner instanceof Class) ownerClass = (Class) owner;
513                       MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
514                       return ownerMetaClass.invokeMethod(owner,methodName,arguments);
515                   }
516               } else if ("curry".equals(methodName)) {
517                   return closure.curry(arguments);
518               }
519    
520               if (method==null && owner!=closure) {
521                   Class ownerClass = owner.getClass();
522                   if (owner instanceof Class) ownerClass = (Class) owner;
523                   MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
524                   method = ownerMetaClass.retrieveMethod(methodName,argClasses);
525                   if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
526               }
527               if (method==null && delegate!=closure && delegate!=null) {
528                   Class delegateClass = delegate.getClass();
529                   if (delegate instanceof Class) delegateClass = (Class) delegate;
530                   MetaClass delegateMetaClass = registry.getMetaClass(delegateClass);
531                   method = delegateMetaClass.retrieveMethod(methodName,argClasses);
532                   if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
533               }
534               if (method==null) {
535                   // still no methods found, test if delegate or owner are GroovyObjects
536                   // and invoke the method on them if so.
537                   MissingMethodException last = null;
538                   if (owner!=closure && (owner instanceof GroovyObject)) {
539                       try {
540                           GroovyObject go = (GroovyObject) owner;
541                           return go.invokeMethod(methodName,originalArguments);
542                       } catch (MissingMethodException mme) {
543                           if (last==null) last = mme;
544                       }
545                   }
546                   if (delegate!=closure && (delegate instanceof GroovyObject)) {
547                       try {
548                           GroovyObject go = (GroovyObject) delegate;
549                           return go.invokeMethod(methodName,originalArguments);
550                       } catch (MissingMethodException mme) {
551                           last = mme;
552                       }
553                   }
554                   if (last!=null) throw last;
555               }
556    
557           }
558    
559           if (method != null) {
560               return MetaClassHelper.doMethodInvoke(object, method, arguments);
561           } else {
562               // if no method was found, try to find a closure defined as a field of the class and run it
563               try {
564                   Object value = this.getProperty(object, methodName);
565                   if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
566                       Closure closure = (Closure) value;
567                       MetaClass delegateMetaClass = closure.getMetaClass();
568                       return delegateMetaClass.invokeMethod(closure.getClass(),closure,"doCall",originalArguments,false,fromInsideClass);
569                   }
570               } catch (MissingPropertyException mpe) {}
571    
572               throw new MissingMethodException(methodName, theClass, originalArguments, false);
573           }
574       }
575       
576       public MetaMethod retrieveMethod(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
577           // lets try use the cache to find the method
578           if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) {
579               return pickMethod(sender, methodName, arguments, isCallToSuper);
580           } else {
581               //TODO: add isSuperCall to key
582               MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments);
583               MetaMethod method = (MetaMethod) methodCache.get(methodKey);
584               if (method == null) {
585                   method = pickMethod(sender, methodName, arguments, isCallToSuper);
586                   cacheInstanceMethod(methodKey, method);
587               }
588               return method;
589           }
590       }
591       
592       protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
593           if (method != null && method.isCacheable()) {
594               methodCache.put(key, method);
595           }
596       }
597    
598       protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
599           if (method != null && method.isCacheable()) {
600               staticMethodCache.put(key, method);
601           }
602       }
603    
604       
605       public Constructor retrieveConstructor(Class[] arguments) {
606           Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
607           if (constructor != null) {
608               return constructor;
609           }
610           constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
611           if (constructor != null) {
612               return constructor;
613           }
614           return null;
615       }
616    
617       public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
618           MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments);
619           MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
620           if (method == null) {
621               method = pickStaticMethod(theClass,methodName, arguments);
622               cacheStaticMethod(methodKey, method);
623           }
624           return method;
625       }
626    
627       
628       
629       /**
630        * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
631        * this method is used only by ClassGenerator for static binding
632        * @param methodName
633        * @param arguments
634        */
635       public MetaMethod pickMethod(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
636           MetaMethod method = null;
637           List methods = getMethods(sender,methodName,isCallToSuper);
638           if (methods!=null && !methods.isEmpty()) {
639               method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
640           }
641           return method;
642       }
643    
644       public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
645           checkInitalised();
646           if (log.isLoggable(Level.FINER)){
647               MetaClassHelper.logMethodCall(object, methodName, arguments);
648           }
649           
650           Class sender = object.getClass();
651           if (object instanceof Class) sender = (Class) object;
652           if (sender!=theClass) {
653               MetaClass mc = registry.getMetaClass(sender);
654               return mc.invokeStaticMethod(sender,methodName,arguments);
655           }
656           if (sender==Class.class) {
657               return invokeMethod(object,methodName,arguments);
658           }
659           
660           if (arguments==null) arguments = EMPTY_ARGUMENTS;
661           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
662           unwrap(arguments);
663           
664           // lets try use the cache to find the method
665           MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses);
666           MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
667           if (method == null) {
668               method = pickStaticMethod(sender, methodName, argClasses);
669               cacheStaticMethod(methodKey.createCopy(), method);
670           }
671    
672           if (method != null) {
673               return MetaClassHelper.doMethodInvoke(object, method, arguments);
674           }
675    
676           throw new MissingMethodException(methodName, sender, arguments, true);
677       }
678       
679       private MetaMethod pickStaticMethod(Class sender, String methodName, Class[] arguments) {
680           MetaMethod method = null;
681           List methods = getStaticMethods(sender,methodName);
682    
683           if (!methods.isEmpty()) {
684               method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
685           }
686           if (method == null && theClass != Class.class) {
687               MetaClass classMetaClass = registry.getMetaClass(Class.class);
688               method = classMetaClass.pickMethod(methodName, arguments);
689           }
690           if (method == null) {
691               method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
692           }
693           return method;
694       }
695    
696       public Object invokeConstructor(Object[] arguments) {
697           return invokeConstructor(theClass,arguments,false);
698       }
699    
700       public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
701           //TODO: that is just a quick prototype, not the real thing!
702           if (numberOfCosntructors != constructors.size()) {
703               throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for "+
704                   this.theClass.getName()+" do not match. Expected "+numberOfCosntructors+" but got "+constructors.size());
705           }
706           
707           if (arguments==null) arguments = EMPTY_ARGUMENTS;
708           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
709           unwrap(arguments);       
710           Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
711           if (constructor == null) {
712               constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
713           }
714           if (constructor==null) {
715               throw new GroovyRuntimeException(
716                       "Could not find matching constructor for: "
717                           + theClass.getName()
718                           + "("+InvokerHelper.toTypeString(arguments)+")");
719           }
720           List l = new ArrayList(constructors);
721           Comparator comp = new Comparator() {
722               public int compare(Object arg0, Object arg1) {
723                   Constructor c0 = (Constructor) arg0;
724                   Constructor c1 = (Constructor) arg1;
725                   String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getParameterTypes()); 
726                   String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getParameterTypes());
727                   return descriptor0.compareTo(descriptor1);
728               }            
729           };
730           Collections.sort(l,comp);
731           int found = -1;
732           for (int i=0; i<l.size(); i++) {
733               if (l.get(i)!=constructor) continue;
734               found = i;
735               break;
736           }
737           // NOTE: must be changed to "1 |" if constructor was vargs
738           int ret = 0 | (found << 8);
739           return ret;
740       }
741       
742       /**
743        * checks if the initialisation of the class id complete.
744        * This method should be called as a form of assert, it is no
745        * way to test if there is still initialisation work to be done. 
746        * Such logic must be implemented in a different way.
747        * @throws IllegalStateException if the initialisation is incomplete yet
748        */
749       protected void checkInitalised() {
750           if (!isInitialized())
751               throw new IllegalStateException(
752                       "initialize must be called for meta " +
753                       "class of "+ theClass + 
754                       "("+this.getClass() + ") " +
755                       "to complete initialisation process " +
756                       "before any invocation or field/property " +
757                       "access can be done");
758       }
759       
760       private Object invokeConstructor(Class at, Object[] arguments, boolean setAccessible) {
761           checkInitalised();
762           if (arguments==null) arguments = EMPTY_ARGUMENTS;
763           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
764           unwrap(arguments);       
765           Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
766           if (constructor != null) {
767               return doConstructorInvoke(at, constructor, arguments, true);
768           }
769           constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
770           if (constructor != null) {
771               return doConstructorInvoke(at, constructor, arguments, true);
772           }
773    
774           if (arguments.length == 1) {
775               Object firstArgument = arguments[0];
776               if (firstArgument instanceof Map) {
777                   constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
778                   if (constructor != null) {
779                       Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true);
780                       setProperties(bean, ((Map) firstArgument));
781                       return bean;
782                   }
783               }
784           }
785           throw new GroovyRuntimeException(
786                       "Could not find matching constructor for: "
787                           + theClass.getName()
788                           + "("+InvokerHelper.toTypeString(arguments)+")");
789       }
790    
791       /**
792        * Sets a number of bean properties from the given Map where the keys are
793        * the String names of properties and the values are the values of the
794        * properties to set
795        */
796       public void setProperties(Object bean, Map map) {
797           checkInitalised();
798           for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
799               Map.Entry entry = (Map.Entry) iter.next();
800               String key = entry.getKey().toString();
801    
802               Object value = entry.getValue();
803               setProperty(bean, key, value);
804           }
805       }
806       
807       /**
808        * @return the given property's value on the object
809        */
810       public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
811           checkInitalised();
812           
813           //----------------------------------------------------------------------
814           // handling of static
815           //----------------------------------------------------------------------
816           boolean isStatic = theClass != Class.class && object instanceof Class;
817           if (isStatic && object != theClass) {
818               MetaClass mc = registry.getMetaClass((Class) object);
819               return mc.getProperty(sender,object,name,useSuper,false);
820           }
821        
822           MetaMethod method = null;
823           Object[] arguments = EMPTY_ARGUMENTS;
824    
825           //----------------------------------------------------------------------
826           // getter
827           //----------------------------------------------------------------------
828           MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
829           if (mp != null) {
830               if (mp instanceof MetaBeanProperty) {
831                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
832                   method = mbp.getGetter();
833                   mp = mbp.getField();
834               } 
835           }
836    
837           // check for a category method named like a getter 
838           if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
839               String getterName = "get"+MetaClassHelper.capitalize(name);
840               method = getCategoryMethodGetter(sender,getterName,false);
841           }
842    
843           //----------------------------------------------------------------------
844           // field
845           //----------------------------------------------------------------------
846           if (method==null && mp!=null) {
847               return mp.getProperty(object);
848           }
849           
850    
851           //----------------------------------------------------------------------
852           // generic get method
853           //----------------------------------------------------------------------       
854           // check for a generic get method provided through a category
855           if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
856               method = getCategoryMethodGetter(sender,"get",true);
857               if (method!=null) arguments = new Object[]{name};
858           }
859    
860           // the generic method is valid, if available (!=null), if static or
861           // if it is not static and we do no static access
862           if (method==null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
863               arguments = new Object[]{ name };
864               method = genericGetMethod;
865           } 
866           
867           //----------------------------------------------------------------------
868           // special cases
869           //----------------------------------------------------------------------
870           if (method==null) {
871               /** todo these special cases should be special MetaClasses maybe */
872               if (theClass != Class.class && object instanceof Class) {
873                   MetaClass mc = registry.getMetaClass(Class.class);
874                   return mc.getProperty(Class.class,object,name,useSuper,false);
875               } else if (object instanceof Collection) {
876                   return DefaultGroovyMethods.getAt((Collection) object, name);
877               } else if (object instanceof Object[]) {
878                   return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
879               } else {
880                   MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
881                   if (addListenerMethod != null) {
882                       //TODO: one day we could try return the previously registered Closure listener for easy removal
883                       return null;
884                   }
885               }
886           } else {
887               
888               //----------------------------------------------------------------------
889               // executing the getter method 
890               //----------------------------------------------------------------------
891               return MetaClassHelper.doMethodInvoke(object,method,arguments);
892           }
893           
894           //----------------------------------------------------------------------
895           // error due to missing method/field
896           //----------------------------------------------------------------------
897           throw new MissingPropertyException(name, theClass);   
898       }
899    
900       private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
901           List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
902           if (possibleGenericMethods != null) {
903               for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
904                   MetaMethod mmethod = (MetaMethod) iter.next();
905                   Class[] paramTypes = mmethod.getParameterTypes();
906                   if (useLongVersion) {
907                       if (paramTypes.length==1 && paramTypes[0] == String.class) {
908                           return mmethod;
909                       }
910                   } else {
911                       if (paramTypes.length==0) return mmethod;
912                   }
913               }
914           }
915           return null;
916       }
917       
918       private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
919           List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
920           if (possibleGenericMethods != null) {
921               for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
922                   MetaMethod mmethod = (MetaMethod) iter.next();
923                   Class[] paramTypes = mmethod.getParameterTypes();
924                   if (useLongVersion) {
925                       if (paramTypes.length==2 && paramTypes[0] == String.class) {
926                           return mmethod;
927                       }
928                   } else {
929                       if (paramTypes.length==1) return mmethod;
930                   }
931               }
932           }
933           return null;
934       }
935    
936       /**
937        * Get all the properties defined for this type
938        * @return a list of MetaProperty objects
939        */
940       public List getProperties() {
941           checkInitalised();
942           Map propertyMap = (Map) classPropertyIndex.get(theClass);
943           // simply return the values of the metaproperty map as a List
944           List ret = new ArrayList(propertyMap.size());
945           for (Iterator iter = propertyMap.values().iterator(); iter.hasNext();) {
946               MetaProperty element = (MetaProperty) iter.next();
947               if (element instanceof MetaFieldProperty) continue;
948               // filter out DGM beans
949               if (element instanceof MetaBeanProperty) {
950                   MetaBeanProperty mp = (MetaBeanProperty) element;
951                   boolean setter = true;
952                   boolean getter = true;
953                   if (mp.getGetter()==null || mp.getGetter() instanceof NewInstanceMetaMethod) {
954                       getter=false;
955                   }
956                   if (mp.getSetter()==null || mp.getSetter() instanceof NewInstanceMetaMethod) {
957                       setter=false;
958                   }
959                   if (!setter && !getter) continue;
960                   if (!setter && mp.getSetter()!=null) {
961                       element = new MetaBeanProperty(mp.getName(),mp.getType(),mp.getGetter(),null);
962                   }
963                   if (!getter && mp.getGetter()!=null) {
964                       element = new MetaBeanProperty(mp.getName(),mp.getType(), null, mp.getSetter());
965                   }
966               }
967               ret.add(element);
968           }
969           return ret;
970       }
971       
972       private MetaMethod findPropertyMethod(List methods, boolean isGetter) {
973           LinkedList ret = new LinkedList();
974           for (Iterator iter = methods.iterator(); iter.hasNext();) {
975               MetaMethod element = (MetaMethod) iter.next();
976               if ( !isGetter && 
977                    //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) && 
978                    element.getParameterTypes().length == 1)
979               {
980                   ret.add(element);
981               } 
982               if ( isGetter &&
983                    !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) && 
984                    element.getParameterTypes().length == 0)
985               {
986                   ret.add(element);
987               }
988           }
989           if (ret.size() == 0) return null;
990           if (ret.size() == 1) return (MetaMethod) ret.getFirst();
991           
992           // we found multiple matching methods
993           // this is a problem, because we can use only one
994           // if it is a getter, then use the most general return 
995           // type to decide which method to use. If it is a setter 
996           // we use the type of the first parameter 
997           MetaMethod method = null;
998           int distance = -1;
999           for (Iterator iter = ret.iterator(); iter.hasNext();) {
1000               MetaMethod element = (MetaMethod) iter.next();
1001               Class c;
1002               if (isGetter) {
1003                   c = element.getReturnType();
1004               } else {
1005                   c = element.getParameterTypes()[0];
1006               }
1007               int localDistance = distanceToObject(c);
1008               //TODO: maybe implement the case localDistance==distance
1009               if (distance==-1 || distance>localDistance) {
1010                   distance = localDistance;
1011                   method = element;
1012               } 
1013           }
1014           return method;
1015       }
1016       
1017       private static int distanceToObject(Class c) {
1018           int count;
1019           for (count=0; c!=null; count++) {
1020               c=c.getSuperclass();           
1021           }
1022           return count;
1023       }
1024       
1025       
1026       /**
1027        * This will build up the property map (Map of MetaProperty objects, keyed on
1028        * property name).
1029        */
1030       private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
1031           LinkedList superClasses = getSuperClasses();
1032           Set interfaces = new HashSet();
1033           makeInterfaceSet(theClass,interfaces);
1034           
1035           // if this an Array, then add the special read-only "length" property
1036           if (theClass.isArray()) {
1037               Map map = new HashMap();
1038               map.put("length", arrayLengthProperty);
1039               classPropertyIndex.put(theClass,map);
1040           }
1041                  
1042           inheritStaticInterfaceFields(superClasses, interfaces);       
1043           inheritFields(superClasses);
1044           applyPropertyDescriptors(propertyDescriptors);
1045           
1046           applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex);
1047           applyStrayPropertyMethods(superClasses,classMethodIndexForSuper,classPropertyIndexForSuper);
1048           
1049           copyClassPropertyIndexForSuper();
1050           makeStaticPropertyIndex();
1051       }
1052       
1053       private void makeStaticPropertyIndex() {
1054           Map propertyMap = (Map) classPropertyIndex.get(theClass);
1055           for (Iterator iter = propertyMap.entrySet().iterator(); iter.hasNext();) {
1056               Map.Entry entry = (Map.Entry) iter.next();
1057               MetaProperty mp = (MetaProperty) entry.getValue();
1058               if (mp instanceof MetaFieldProperty) {
1059                   MetaFieldProperty mfp = (MetaFieldProperty) mp;
1060                   if (!mfp.isStatic()) continue;
1061               } else if (mp instanceof MetaBeanProperty) {
1062                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
1063                   boolean getter = mbp.getGetter()==null || mbp.getGetter().isStatic();
1064                   boolean setter = mbp.getSetter()==null || mbp.getSetter().isStatic();
1065                   boolean field = mbp.getField()==null || mbp.getField().isStatic();
1066                   
1067                   if (!getter && !setter && !field) {
1068                       continue;
1069                   } else if (setter && getter) {
1070                       if (field) {
1071                           mp = mbp; // nothing to do
1072                       } else {
1073                           mp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),mbp.getSetter());
1074                       }
1075                   } else if (getter && !setter) {
1076                       if (mbp.getGetter()==null) {
1077                           mp = mbp.getField();
1078                       } else {
1079                           MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),null);
1080                           if (field) newmp.setField(mbp.getField());
1081                           mp = newmp;
1082                       }
1083                   } else if (setter && !getter) {
1084                       if (mbp.getSetter()==null) {
1085                           mp = mbp.getField();
1086                       } else {
1087                           MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),null,mbp.getSetter());
1088                           if (field) newmp.setField(mbp.getField());
1089                           mp = newmp;
1090                       }
1091                   } else if (field) {
1092                       mp = mbp.getField();
1093                   }
1094               } else {
1095                   continue; // ignore all other types
1096               }
1097               if (mp==null) continue;
1098               staticPropertyIndex.put(entry.getKey(),mp);
1099           }
1100           
1101       }
1102       
1103       private void copyClassPropertyIndexForSuper() {
1104           for (Iterator iter = classPropertyIndex.entrySet().iterator(); iter.hasNext();) {
1105               Map.Entry entry = (Map.Entry) iter.next();
1106               HashMap newVal = new HashMap((Map)entry.getValue());
1107               classPropertyIndexForSuper.put(entry.getKey(),newVal);
1108           }
1109       }
1110       
1111       private Map getMap2MapNotNull(Map m, Object key) {
1112           Map ret = (Map) m.get(key);
1113           if (ret==null) {
1114               ret = new HashMap();
1115               m.put(key,ret);
1116           }
1117           return ret;
1118       }
1119       
1120       private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
1121           for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
1122               Class iclass = (Class) interfaceIter.next();
1123               Map iPropertyIndex = getMap2MapNotNull(classPropertyIndex,iclass);
1124               addFields(iclass,iPropertyIndex);
1125               for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
1126                   Class sclass = (Class) classIter.next();
1127                   if (! iclass.isAssignableFrom(sclass)) continue;
1128                   Map sPropertyIndex = getMap2MapNotNull(classPropertyIndex,sclass);
1129                   copyNonPrivateFields(iPropertyIndex,sPropertyIndex);
1130               }
1131           }
1132       }
1133       
1134       private void inheritFields(LinkedList superClasses) {
1135           Map last = null;
1136           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
1137               Class klass = (Class) iter.next();
1138               Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
1139               if (last != null) {
1140                   copyNonPrivateFields(last,propertyIndex);
1141               }
1142               last = propertyIndex;
1143               addFields(klass,propertyIndex);
1144           }   
1145       }
1146       
1147       private void addFields(final Class klass, Map propertyIndex) {
1148           Field[] fields = (Field[]) AccessController.doPrivileged(new  PrivilegedAction() {
1149               public Object run() {
1150                   return klass.getDeclaredFields();
1151               }
1152           });
1153           for(int i = 0; i < fields.length; i++) {
1154               MetaFieldProperty mfp = new MetaFieldProperty(fields[i]);
1155               propertyIndex.put(fields[i].getName(), mfp);
1156           }
1157       }
1158    
1159       private void copyNonPrivateFields(Map from, Map to) {
1160           for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) {
1161               Map.Entry entry = (Map.Entry) iter.next();
1162               MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue();
1163               if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
1164               to.put(entry.getKey(),mfp);
1165           }
1166       }
1167       
1168       private void applyStrayPropertyMethods(LinkedList superClasses, Map classMethodIndex, Map classPropertyIndex) {
1169           // now look for any stray getters that may be used to define a property
1170           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
1171               Class klass = (Class) iter.next();
1172               Map methodIndex = (Map) classMethodIndex.get(klass);
1173               Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
1174               for (Iterator nameMethodIterator = methodIndex.entrySet().iterator(); nameMethodIterator.hasNext();) {
1175                   Map.Entry entry = (Map.Entry) nameMethodIterator.next();
1176                   String methodName = (String) entry.getKey();
1177                   // name too sort?
1178                   if (methodName.length() < 4) continue;
1179                   //possible getter/setter
1180                   boolean isGetter = methodName.startsWith("get");
1181                   boolean isSetter = methodName.startsWith("set");
1182                   if (!isGetter && !isSetter) continue;
1183                   
1184                   // get the name of the property
1185                   String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
1186                   MetaMethod propertyMethod = findPropertyMethod((List) entry.getValue(), isGetter);
1187                   if (propertyMethod==null) continue;
1188                   
1189                   createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
1190               }
1191           }
1192       }
1193       
1194       private void createMetaBeanProperty(Map propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod){
1195           // is this property already accounted for?
1196           MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
1197           if (mp == null) {
1198               if (isGetter) {
1199                   mp = new MetaBeanProperty(propName,
1200                           propertyMethod.getReturnType(),
1201                           propertyMethod, null);
1202               } else {
1203                   //isSetter
1204                   mp = new MetaBeanProperty(propName,
1205                           propertyMethod.getParameterTypes()[0],
1206                           null, propertyMethod);
1207               }
1208           } else {
1209               MetaBeanProperty mbp;
1210               MetaFieldProperty mfp;
1211               if (mp instanceof MetaBeanProperty) {
1212                   mbp = (MetaBeanProperty) mp;
1213                   mfp = mbp.getField();
1214               } else if (mp instanceof MetaFieldProperty){
1215                   mfp = (MetaFieldProperty) mp;
1216                   mbp = new MetaBeanProperty(propName,
1217                           mfp.getType(),
1218                           null, null);
1219               } else {
1220                   throw new GroovyBugError("unknown MetaProperty class used. Class is "+mp.getClass());
1221               }
1222               // we may have already found one for this name
1223               if (isGetter && mbp.getGetter()==null) {
1224                   mbp.setGetter(propertyMethod);
1225               } else if (!isGetter && mbp.getSetter()==null) {
1226                   mbp.setSetter(propertyMethod);
1227               }
1228               mbp.setField(mfp);
1229               mp = mbp;
1230           }
1231           propertyIndex.put(propName, mp);
1232       }
1233    
1234       private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
1235           Map propertyMap = (Map) classPropertyIndex.get(theClass);
1236           // now iterate over the map of property descriptors and generate
1237           // MetaBeanProperty objects
1238           for(int i=0; i<propertyDescriptors.length; i++) {
1239               PropertyDescriptor pd = propertyDescriptors[i];
1240               
1241               // skip if the property type is unknown (this seems to be the case if the
1242               // property descriptor is based on a setX() method that has two parameters,
1243               // which is not a valid property)
1244               if(pd.getPropertyType() == null)
1245                   continue;
1246               
1247               // get the getter method
1248               Method method = pd.getReadMethod();
1249               MetaMethod getter;
1250               if(method != null)
1251                   getter = findMethod(method);
1252               else
1253                   getter = null;
1254               
1255               // get the setter method
1256               MetaMethod setter;
1257               method = pd.getWriteMethod();
1258               if(method != null)
1259                   setter = findMethod(method);
1260               else
1261                   setter = null;
1262               
1263               // now create the MetaProperty object
1264               MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
1265               
1266               //keep field
1267               MetaFieldProperty field = null;
1268               MetaProperty old = (MetaProperty) propertyMap.get(pd.getName());
1269               if (old!=null) {
1270                   if (old instanceof MetaBeanProperty) {
1271                       field = ((MetaBeanProperty) old).getField();
1272                   } else {
1273                       field = (MetaFieldProperty) old;
1274                   }
1275                   mp.setField(field);
1276               }
1277               
1278               // put it in the list
1279               // this will overwrite a possible field property
1280               propertyMap.put(pd.getName(), mp);
1281           }       
1282       }
1283       
1284       /**
1285        * Sets the property value on an object
1286        */
1287       public void setProperty(Class sender,Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
1288           checkInitalised();
1289           
1290           //----------------------------------------------------------------------
1291           // handling of static
1292           //----------------------------------------------------------------------
1293           boolean isStatic = theClass != Class.class && object instanceof Class;
1294           if (isStatic && object != theClass) {
1295               MetaClass mc = registry.getMetaClass((Class) object);
1296               mc.getProperty(sender,object,name,useSuper,fromInsideClass);
1297               return;
1298           }
1299           
1300           //----------------------------------------------------------------------
1301           // Unwrap wrapped values fo now - the new MOP will handle them properly
1302           //----------------------------------------------------------------------
1303           if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap();
1304           
1305           
1306        
1307           MetaMethod method = null;
1308           Object[] arguments = null;
1309    
1310           //----------------------------------------------------------------------
1311           // setter
1312           //----------------------------------------------------------------------
1313           MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
1314           MetaProperty field = null;
1315           if (mp != null) {
1316               if (mp instanceof MetaBeanProperty) {
1317                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
1318                   method = mbp.getSetter();
1319                   if (method!=null) arguments = new Object[] { newValue };
1320                   field = mbp.getField();
1321               } else {
1322                   field = mp;
1323               }
1324           }
1325           
1326           // check for a category method named like a setter 
1327           if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
1328               String getterName = "set"+MetaClassHelper.capitalize(name);
1329               method = getCategoryMethodSetter(sender,getterName,false);
1330               if (method!=null) arguments = new Object[] { newValue };
1331           }
1332    
1333           //----------------------------------------------------------------------
1334           // listener method
1335           //----------------------------------------------------------------------
1336           boolean ambigousListener = false;
1337           boolean usesProxy = false;
1338           if (method==null) {
1339               method = (MetaMethod) listeners.get(name);
1340               ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
1341               if ( method != null && 
1342                    !ambigousListener &&
1343                    newValue instanceof Closure) 
1344               {
1345                   // lets create a dynamic proxy
1346                   Object proxy =
1347                       MetaClassHelper.createListenerProxy(method.getParameterTypes()[0], name, (Closure) newValue);
1348                   arguments = new Object[] { proxy };
1349                   newValue = proxy;
1350                   usesProxy = true;
1351               } else {
1352                   method = null;
1353               }
1354           }
1355           
1356           //----------------------------------------------------------------------
1357           // field
1358           //----------------------------------------------------------------------
1359           if (method==null && field!=null) {
1360               field.setProperty(object,newValue);
1361               return;
1362           }       
1363    
1364           //----------------------------------------------------------------------
1365           // generic set method
1366           //----------------------------------------------------------------------       
1367           // check for a generic get method provided through a category
1368           if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
1369               method = getCategoryMethodSetter(sender,"set",true);
1370               if (method!=null) arguments = new Object[]{name,newValue};
1371           }
1372    
1373           // the generic method is valid, if available (!=null), if static or
1374           // if it is not static and we do no static access
1375           if (method==null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
1376               arguments = new Object[]{ name, newValue };
1377               method = genericSetMethod;
1378           } 
1379           
1380           //----------------------------------------------------------------------
1381           // executing the getter method 
1382           //----------------------------------------------------------------------
1383           if (method!=null) {
1384               if (arguments.length==1) {
1385                   newValue = DefaultTypeTransformation.castToType(
1386                           newValue,
1387                           method.getParameterTypes()[0]);
1388                   arguments[0] = newValue;
1389               } else {
1390                   newValue = DefaultTypeTransformation.castToType(
1391                           newValue,
1392                           method.getParameterTypes()[1]);
1393                   arguments[1] = newValue;
1394               }
1395               MetaClassHelper.doMethodInvoke(object,method,arguments);
1396               return;
1397           }
1398               
1399           //----------------------------------------------------------------------
1400           // error due to missing method/field
1401           //----------------------------------------------------------------------
1402           if (ambigousListener){
1403               throw new GroovyRuntimeException("There are multiple listeners for the property "+name+". Please do not use the bean short form to access this listener.");
1404           } 
1405           throw new MissingPropertyException(name, theClass);   
1406       }
1407       
1408       private MetaProperty getMetaProperty(Class clazz, String name, boolean useSuper, boolean useStatic) {
1409           Map propertyMap;
1410           if (useStatic) {
1411               propertyMap = staticPropertyIndex;
1412           } else if (useSuper){
1413               propertyMap = (Map) classPropertyIndexForSuper.get(clazz);
1414           } else {
1415               propertyMap = (Map) classPropertyIndex.get(clazz);
1416           }
1417           if (propertyMap==null) {
1418               if (clazz!=theClass) {
1419                   return getMetaProperty(theClass,name,useSuper, useStatic);
1420               } else {
1421                   return null;
1422               }           
1423           }
1424           return (MetaProperty) propertyMap.get(name);
1425       }
1426    
1427    
1428       /**
1429        * Looks up the given attribute (field) on the given object
1430        */
1431       public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
1432           checkInitalised();
1433           
1434           boolean isStatic = theClass != Class.class && object instanceof Class;
1435           if (isStatic && object != theClass) {
1436               MetaClass mc = registry.getMetaClass((Class) object);
1437               return mc.getAttribute(sender,object,attribute,useSuper);
1438           }
1439        
1440           MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
1441           
1442           if (mp != null) {
1443               if (mp instanceof MetaBeanProperty) {
1444                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
1445                   mp = mbp.getField();
1446               }
1447               try {
1448                   // delegate the get operation to the metaproperty
1449                   if (mp != null) return mp.getProperty(object);
1450               } catch(Exception e) {
1451                   throw new GroovyRuntimeException("Cannot read field: " + attribute,e);
1452               }
1453           }
1454           
1455           throw new MissingFieldException(attribute, theClass);
1456       }
1457    
1458       /**
1459        * Sets the given attribute (field) on the given object
1460        */
1461       public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
1462           checkInitalised();
1463           
1464           boolean isStatic = theClass != Class.class && object instanceof Class;
1465           if (isStatic && object != theClass) {
1466               MetaClass mc = registry.getMetaClass((Class) object);
1467               mc.setAttribute(sender,object,attribute,newValue,useSuper,fromInsideClass);
1468               return;
1469           }
1470        
1471           MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
1472           
1473           if (mp != null) {
1474               if (mp instanceof MetaBeanProperty) {
1475                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
1476                   mp = mbp.getField();
1477               }
1478               if (mp != null) {
1479                   mp.setProperty(object,newValue);
1480                   return;
1481               }
1482           }
1483           
1484           throw new MissingFieldException(attribute, theClass);
1485       }
1486    
1487       public ClassNode getClassNode() {
1488           if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
1489               // lets try load it from the classpath
1490               String className = theClass.getName();
1491               String groovyFile = className;
1492               int idx = groovyFile.indexOf('$');
1493               if (idx > 0) {
1494                   groovyFile = groovyFile.substring(0, idx);
1495               }
1496               groovyFile = groovyFile.replace('.', '/') + ".groovy";
1497    
1498               //System.out.println("Attempting to load: " + groovyFile);
1499               URL url = theClass.getClassLoader().getResource(groovyFile);
1500               if (url == null) {
1501                   url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
1502               }
1503               if (url != null) {
1504                   try {
1505    
1506                       /**
1507                        * todo there is no CompileUnit in scope so class name
1508                        * checking won't work but that mostly affects the bytecode
1509                        * generation rather than viewing the AST
1510                        */
1511                       CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1512                           public void call( ClassVisitor writer, ClassNode node ) {
1513                               if( node.getName().equals(theClass.getName()) ) {
1514                                   MetaClassImpl.this.classNode = node;
1515                               }
1516                           }
1517                       };
1518    
1519                       final ClassLoader parent = theClass.getClassLoader();
1520                       GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
1521                           public Object run() {
1522                               return new GroovyClassLoader(parent);
1523                           }
1524                       });
1525                       CompilationUnit unit = new CompilationUnit( );
1526                       unit.setClassgenCallback( search );
1527                       unit.addSource( url );
1528                       unit.compile( Phases.CLASS_GENERATION );
1529                   }
1530                   catch (Exception e) {
1531                       throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
1532                   }
1533               }
1534    
1535           }
1536           return classNode;
1537       }
1538    
1539       public String toString() {
1540           return super.toString() + "[" + theClass + "]";
1541       }
1542    
1543       // Implementation methods
1544       //-------------------------------------------------------------------------
1545       
1546       /**
1547        * Adds all the methods declared in the given class to the metaclass
1548        * ignoring any matching methods already defined by a derived class
1549        *
1550        * @param theClass
1551        */
1552       private void addMethods(final Class theClass) {
1553           Map methodIndex = (Map) classMethodIndex.get(theClass);
1554           if (methodIndex==null) {
1555               methodIndex = new HashMap();
1556               classMethodIndex.put(theClass,methodIndex);
1557           }
1558           // add methods directly declared in the class
1559           Method[] methodArray = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
1560                   public Object run() {
1561                       return theClass.getDeclaredMethods();
1562                   }
1563               });
1564           for (int i = 0; i < methodArray.length; i++) {
1565               Method reflectionMethod = methodArray[i];
1566               if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
1567                   // Skip Synthetic methods inserted by JDK 1.5 compilers and later
1568                   continue;
1569               } else if (Modifier.isAbstract(reflectionMethod.getModifiers())) {
1570                   continue;
1571               }
1572               MetaMethod method = createMetaMethod(reflectionMethod);
1573               addMetaMethod(method);
1574           }
1575           // add methods declared by DGM
1576           List methods = registry.getInstanceMethods();
1577           for (Iterator iter = methods.iterator(); iter.hasNext();) {
1578               Method element = (Method) iter.next();
1579               if (element.getParameterTypes()[0]!=theClass) continue;
1580               addNewInstanceMethod(element);
1581           }
1582           // add static methods declared by DGM
1583           methods = registry.getStaticMethods();
1584           for (Iterator iter = methods.iterator(); iter.hasNext();) {
1585               Method element = (Method) iter.next();
1586               if (element.getParameterTypes()[0]!=theClass) continue;
1587               addNewStaticMethod(element);
1588           }
1589       }
1590       
1591       private void addToClassMethodIndex(MetaMethod method, Map classMethodIndex) {
1592           Map methodIndex = (Map) classMethodIndex.get(method.getDeclaringClass());
1593           if (methodIndex==null) {
1594               methodIndex = new HashMap();
1595               classMethodIndex.put(method.getDeclaringClass(),methodIndex);
1596           }
1597           String name = method.getName();
1598           List list = (List) methodIndex.get(name);
1599           if (list == null) {
1600               list = new ArrayList();
1601               methodIndex.put(name, list);
1602               list.add(method);
1603           } else {
1604               addMethodToList(list,method);
1605           }
1606       }
1607    
1608       /**
1609        * adds a MetaMethod to this class. WARNING: this method will not
1610        * do the neccessary steps for multimethod logic and using this
1611        * method doesn't mean, that a method added here is replacing another
1612        * method from a parent class completely. These steps are usually done
1613        * by initalize, which means if you need these steps, you have to add
1614        * the method before running initialize the first time.
1615        * @see #initialize() 
1616        * @param method the MetaMethod
1617        */
1618       protected void addMetaMethod(MetaMethod method) {
1619           if (isInitialized()) {
1620               throw new RuntimeException("Already initialized, cannot add new method: " + method);
1621           }
1622           if (isGenericGetMethod(method) && genericGetMethod == null) {
1623               genericGetMethod = method;
1624           }
1625           else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
1626               genericSetMethod = method;
1627           }
1628           if (method.isStatic()) {
1629               addToClassMethodIndex(method,classStaticMethodIndex);
1630           }
1631           addToClassMethodIndex(method,classMethodIndex);
1632       }
1633       
1634       protected boolean isInitialized(){
1635           return initialized;
1636       }
1637       
1638       private void addMethodToList(List list, MetaMethod method) {
1639           MetaMethod match = removeMatchingMethod(list,method);
1640           if (match==null) {
1641               list.add(method);
1642           } else if (match.isPrivate()){
1643               // do not overwrite private methods
1644               // Note: private methods from parent classes are not shown here,
1645               // but when doing the multimethod connection step, we overwrite
1646               // methods of the parent class with methods of a subclass and
1647               // in that case we want to keep the private methods
1648               list.add(match);
1649           } else {
1650               Class methodC = method.getDeclaringClass();
1651               Class matchC = match.getDeclaringClass();
1652               if (methodC == matchC){
1653                   if (method instanceof NewInstanceMetaMethod) {
1654                       // let DGM replace existing methods
1655                       list.add(method);
1656                   } else {
1657                       list.add(match);
1658                   }               
1659               } else if (MetaClassHelper.isAssignableFrom(methodC,matchC)){
1660                   list.add(match);
1661               } else {
1662                  list.add(method);
1663               }
1664           }
1665       }
1666       
1667       /**
1668        * remove a method of the same matching prototype was found in the list
1669        */
1670       private MetaMethod removeMatchingMethod(List list, MetaMethod method) {
1671           for (Iterator iter = list.iterator(); iter.hasNext();) {
1672               MetaMethod aMethod = (MetaMethod) iter.next();
1673               Class[] params1 = aMethod.getParameterTypes();
1674               Class[] params2 = method.getParameterTypes();
1675               if (params1.length == params2.length) {
1676                   boolean matches = true;
1677                   for (int i = 0; i < params1.length; i++) {
1678                       if (params1[i] != params2[i]) {
1679                           matches = false;
1680                           break;
1681                       }
1682                   }
1683                   if (matches) {
1684                       iter.remove();
1685                       return (MetaMethod) aMethod;
1686                   }
1687               }
1688           }
1689           return null;
1690       }
1691    
1692       /**
1693        * @return the matching method which should be found
1694        */
1695       private MetaMethod findMethod(Method aMethod) {
1696           List methods = getMethods(theClass,aMethod.getName(),false);
1697           for (Iterator iter = methods.iterator(); iter.hasNext();) {
1698               MetaMethod method = (MetaMethod) iter.next();
1699               if (method.isMethod(aMethod)) {
1700                   return method;
1701               }
1702           }
1703           //log.warning("Creating reflection based dispatcher for: " + aMethod);
1704           return new ReflectionMetaMethod(aMethod);
1705       }
1706    
1707       /**
1708        * @return the getter method for the given object
1709        */
1710       private MetaMethod findGetter(Object object, String name) {
1711           List methods = getMethods(theClass,name,false);
1712           for (Iterator iter = methods.iterator(); iter.hasNext();) {
1713               MetaMethod method = (MetaMethod) iter.next();
1714               if (method.getParameterTypes().length == 0) {
1715                   return method;
1716               }
1717           }
1718           return null;
1719       }
1720    
1721       /**
1722        * @return the Method of the given name with no parameters or null
1723        */
1724       private MetaMethod findStaticGetter(Class type, String name) {
1725           List methods = getStaticMethods(type, name);
1726           for (Iterator iter = methods.iterator(); iter.hasNext();) {
1727               MetaMethod method = (MetaMethod) iter.next();
1728               if (method.getParameterTypes().length == 0) {
1729                   return method;
1730               }
1731           }
1732    
1733           /** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1734           try {
1735               Method method = type.getMethod(name, MetaClassHelper.EMPTY_TYPE_ARRAY);
1736               if ((method.getModifiers() & Modifier.STATIC) != 0) {
1737                   return findMethod(method);
1738               }
1739               else {
1740                   return null;
1741               }
1742           }
1743           catch (Exception e) {
1744               return null;
1745           }
1746       }
1747       
1748       private static Object doConstructorInvoke(final Class at, Constructor constructor, Object[] argumentArray, boolean setAccessible) {
1749           if (log.isLoggable(Level.FINER)) {
1750               MetaClassHelper.logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
1751           }
1752    
1753           if (setAccessible) {
1754               // To fix JIRA 435
1755               // Every constructor should be opened to the accessible classes.
1756               final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
1757               final Constructor ctor = constructor;
1758               AccessController.doPrivileged(new PrivilegedAction() {
1759                   public Object run() {
1760                       ctor.setAccessible(accessible);
1761                       return null;
1762                   }
1763               });
1764           }
1765           return MetaClassHelper.doConstructorInvoke(constructor,argumentArray);
1766       }
1767    
1768       /**
1769        * Chooses the correct method to use from a list of methods which match by
1770        * name.
1771        *
1772        * @param methods
1773        *            the possible methods to choose from
1774        * @param arguments
1775        *            the original argument to the method
1776        */
1777       private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
1778           int methodCount = methods.size();
1779           if (methodCount <= 0) {
1780               return null;
1781           }
1782           else if (methodCount == 1) {
1783               Object method = methods.get(0);
1784               if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
1785                   return method;
1786               }
1787               return null;
1788           }
1789           Object answer = null;
1790           if (arguments == null || arguments.length == 0) {
1791               answer = MetaClassHelper.chooseEmptyMethodParams(methods);
1792           }
1793           else if (arguments.length == 1 && arguments[0] == null) {
1794               answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
1795           }
1796           else {
1797               List matchingMethods = new ArrayList();
1798    
1799               for (Iterator iter = methods.iterator(); iter.hasNext();) {
1800                   Object method = iter.next();
1801    
1802                   // making this false helps find matches
1803                   if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
1804                       matchingMethods.add(method);
1805                   }
1806               }
1807               if (matchingMethods.isEmpty()) {
1808                   return null;
1809               }
1810               else if (matchingMethods.size() == 1) {
1811                   return matchingMethods.get(0);
1812               }
1813               return chooseMostSpecificParams(methodName, matchingMethods, arguments);
1814    
1815           }
1816           if (answer != null) {
1817               return answer;
1818           }
1819           throw new GroovyRuntimeException(
1820               "Could not find which method to invoke from this list: "
1821                   + methods
1822                   + " for arguments: "
1823                   + InvokerHelper.toString(arguments));
1824       }
1825    
1826       private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
1827    
1828           long matchesDistance = -1;
1829           LinkedList matches = new LinkedList();
1830           for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1831               Object method = iter.next();
1832               Class[] paramTypes = MetaClassHelper.getParameterTypes(method);
1833               if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
1834               long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
1835               if (dist==0) return method;
1836               if (matches.size()==0) {
1837                   matches.add(method);
1838                   matchesDistance = dist;
1839               } else if (dist<matchesDistance) {
1840                   matchesDistance=dist;
1841                   matches.clear();
1842                   matches.add(method);
1843               } else if (dist==matchesDistance) {
1844                   matches.add(method);
1845               }
1846    
1847           }
1848           if (matches.size()==1) {
1849               return matches.getFirst();
1850           }
1851           if (matches.size()==0) {
1852               return null;
1853           }
1854    
1855           //more than one matching method found --> ambigous!
1856           String msg = "Ambiguous method overloading for method ";
1857           msg+= theClass.getName()+"#"+name;
1858           msg+= ".\nCannot resolve which method to invoke for ";
1859           msg+= InvokerHelper.toString(arguments);
1860           msg+= " due to overlapping prototypes between:";
1861           for (Iterator iter = matches.iterator(); iter.hasNext();) {
1862               Class[] types=MetaClassHelper.getParameterTypes(iter.next());
1863               msg+= "\n\t"+InvokerHelper.toString(types);
1864           }
1865           throw new GroovyRuntimeException(msg);
1866       }
1867    
1868       private boolean isGenericGetMethod(MetaMethod method) {
1869           if (method.getName().equals("get")) {
1870               Class[] parameterTypes = method.getParameterTypes();
1871               return parameterTypes.length == 1 && parameterTypes[0] == String.class;
1872           }
1873           return false;
1874       }
1875    
1876       /**
1877        * Call this method when any mutation method is called, such as adding a new
1878        * method to this MetaClass so that any caching or bytecode generation can be
1879        * regenerated.
1880        */
1881       private synchronized void onMethodChange() {
1882           reflector = null;
1883       }
1884    
1885       
1886       public synchronized void initialize() {
1887           if (!isInitialized()) {
1888               fillMethodIndex();
1889               addProperties();
1890               initialized = true;
1891           }
1892           if (reflector == null) {
1893               generateReflector();
1894           }
1895       }
1896    
1897       private void addProperties()  {
1898           BeanInfo info;
1899           //     introspect
1900           try {
1901               info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
1902                   public Object run() throws IntrospectionException {
1903                       return Introspector.getBeanInfo(theClass);
1904                   }
1905               });
1906           } catch (PrivilegedActionException pae) {
1907               throw new GroovyRuntimeException("exception while bean introspection",pae.getException());
1908           }
1909           PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
1910    
1911           // build up the metaproperties based on the public fields, property descriptors,
1912           // and the getters and setters
1913           setupProperties(descriptors);
1914           
1915           EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
1916           for (int i = 0; i < eventDescriptors.length; i++) {
1917               EventSetDescriptor descriptor = eventDescriptors[i];
1918               Method[] listenerMethods = descriptor.getListenerMethods();
1919               for (int j = 0; j < listenerMethods.length; j++) {
1920                   Method listenerMethod = listenerMethods[j];
1921                   MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
1922                   String name = listenerMethod.getName();
1923                   if (listeners.containsKey(name)) {
1924                       listeners.put(name, AMBIGOUS_LISTENER_METHOD);
1925                   } else{
1926                       listeners.put(name, metaMethod);
1927                   }
1928               }
1929           }    
1930       }
1931    
1932       private MetaMethod createMetaMethod(final Method method) {
1933           if (registry.useAccessible()) {
1934               AccessController.doPrivileged(new PrivilegedAction() {
1935                   public Object run() {
1936                       method.setAccessible(true);
1937                       return null;
1938                   }
1939               });
1940           }
1941    
1942           MetaMethod answer = new MetaMethod(method);
1943           if (isValidReflectorMethod(answer)) {
1944               allMethods.add(answer);
1945               answer.setMethodIndex(allMethods.size());
1946           }
1947           else {
1948               //log.warning("Creating reflection based dispatcher for: " + method);
1949               answer = new ReflectionMetaMethod(method);
1950           }
1951    
1952           if (useReflection) {
1953               //log.warning("Creating reflection based dispatcher for: " + method);
1954               return new ReflectionMetaMethod(method);
1955           }
1956    
1957           return answer;
1958       }
1959    
1960       private boolean isValidReflectorMethod(MetaMethod method) {
1961           // We cannot use a reflector if the method is private, protected, or package accessible only.
1962           if (!method.isPublic()) {
1963               return false;
1964           }
1965           // lets see if this method is implemented on an interface
1966           List interfaceMethods = getInterfaceMethods();
1967           for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) {
1968               MetaMethod aMethod = (MetaMethod) iter.next();
1969               if (method.isSame(aMethod)) {
1970                   method.setInterfaceClass(aMethod.getCallClass());
1971                   return true;
1972               }
1973           }
1974           // it's no interface method, so try to find the highest class
1975           // in hierarchy defining this method
1976           Class declaringClass = method.getCallClass();
1977           for (Class clazz=declaringClass; clazz!=null; clazz=clazz.getSuperclass()) {
1978               try {
1979                   final Class klazz = clazz;
1980                   final String mName = method.getName();
1981                   final Class[] parms = method.getParameterTypes();
1982                   try {
1983                       Method m = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
1984                           public Object run() throws NoSuchMethodException {
1985                               return klazz.getDeclaredMethod(mName, parms);
1986                           }
1987                       });
1988                       if (!Modifier.isPublic(clazz.getModifiers())) continue;
1989                       if (!Modifier.isPublic(m.getModifiers())) continue;
1990                       declaringClass = clazz;
1991                   } catch (PrivilegedActionException pae) {
1992                       if (pae.getException() instanceof NoSuchMethodException) {
1993                           throw (NoSuchMethodException) pae.getException();
1994                       } else {
1995                           throw new RuntimeException(pae.getException());
1996                       }
1997                   }
1998               } catch (SecurityException e) {
1999                   continue;
2000               } catch (NoSuchMethodException e) {
2001                   continue;
2002               }
2003           }
2004           if (!Modifier.isPublic(declaringClass.getModifiers())) return false;
2005           method.setCallClass(declaringClass);
2006    
2007           return true;
2008       }
2009    
2010       private void generateReflector() {
2011           reflector = registry.loadReflector(theClass, allMethods);
2012           if (reflector == null) {
2013               throw new RuntimeException("Should have a reflector for "+theClass.getName());
2014           }
2015           // lets set the reflector on all the methods
2016           for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
2017               MetaMethod metaMethod = (MetaMethod) iter.next();
2018               metaMethod.setReflector(reflector);
2019           }
2020       }
2021    
2022       public List getMethods() {
2023           return allMethods;
2024       }
2025    
2026       public List getMetaMethods() {
2027           return new ArrayList(newGroovyMethodsList);
2028       }
2029    
2030       private synchronized List getInterfaceMethods() {
2031           if (interfaceMethods == null) {
2032               interfaceMethods = new ArrayList();
2033               Class type = theClass;
2034               while (type != null) {
2035                   Class[] interfaces = type.getInterfaces();
2036                   for (int i = 0; i < interfaces.length; i++) {
2037                       Class iface = interfaces[i];
2038                       Method[] methods = iface.getMethods();
2039                       addInterfaceMethods(interfaceMethods, methods);
2040                   }
2041                   type = type.getSuperclass();
2042               }
2043           }
2044           return interfaceMethods;
2045       }
2046    
2047       private void addInterfaceMethods(List list, Method[] methods) {
2048           for (int i = 0; i < methods.length; i++) {
2049               list.add(createMetaMethod(methods[i]));
2050           }
2051       }
2052       
2053       private static class MethodIndexAction {
2054           public void iterate(Map classMethodIndex){
2055               for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) {
2056                   Map.Entry classEntry = (Map.Entry) iter.next();
2057                   Map methodIndex = (Map) classEntry.getValue();
2058                   Class clazz = (Class) classEntry.getKey();
2059                   if (skipClass(clazz)) continue;               
2060                   for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
2061                       Map.Entry nameEntry = (Map.Entry) iterator.next();
2062                       String name = (String) nameEntry.getKey();
2063                       List oldList = (List) nameEntry.getValue();
2064                       List newList = methodNameAction(clazz, name, oldList);
2065                       if (replaceMethodList()) nameEntry.setValue(newList); 
2066                   }
2067               }
2068           }
2069           public List methodNameAction(Class clazz, String methodName, List methods) {
2070               List newList = new ArrayList(methods.size());
2071               for (Iterator methodIter = methods.iterator(); methodIter.hasNext();) {
2072                   MetaMethod method = (MetaMethod) methodIter.next();
2073                   methodListAction(clazz,methodName,method,methods,newList);
2074               }
2075               return newList;
2076           }
2077           public boolean skipClass(Class clazz) {return false;}
2078           public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {}
2079           public boolean replaceMethodList(){return true;}
2080       }
2081    
2082       /**
2083        * @deprecated
2084        */
2085       public Object getProperty(Object object, String property) {
2086           return getProperty(theClass,object,property,false,false);
2087       }
2088       
2089       /**
2090        * @deprecated
2091        */
2092       public void setProperty(Object object, String property, Object newValue) {
2093           setProperty(theClass,object,property,newValue,false,false);
2094       }
2095       
2096       /**
2097        * @deprecated
2098        */
2099       public Object getAttribute(Object object, String attribute) {
2100           return getAttribute(theClass,object,attribute,false,false);
2101       }
2102       
2103       /**
2104        * @deprecated
2105        */
2106       public void setAttribute(Object object, String attribute, Object newValue) {
2107           setAttribute(theClass,object,attribute,newValue,false,false);
2108       }
2109    
2110       public MetaMethod pickMethod(String methodName, Class[] arguments) {
2111           return pickMethod(theClass,methodName,arguments,false);
2112       }
2113       
2114       protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
2115           return retrieveMethod(theClass,methodName,arguments,false);
2116       }
2117       
2118       /**
2119        * remove all method call cache entries. This should be done if a 
2120        * method is added during runtime, but not by using a category.
2121        */
2122       protected void clearInvocationCaches() {
2123           staticMethodCache.clear();
2124           methodCache.clear();
2125       }
2126    }