Save This Page
Home » groovy-src-1.6.5 » groovy » lang » [javadoc | source]
    1   /*
    2    * Copyright 2003-2009 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   package groovy.lang;
   17   
   18   import org.codehaus.groovy.GroovyBugError;
   19   import org.codehaus.groovy.ast.ClassNode;
   20   import org.codehaus.groovy.classgen.BytecodeHelper;
   21   import org.codehaus.groovy.control.CompilationUnit;
   22   import org.codehaus.groovy.control.Phases;
   23   import org.codehaus.groovy.reflection.CachedClass;
   24   import org.codehaus.groovy.reflection.CachedConstructor;
   25   import org.codehaus.groovy.reflection.CachedField;
   26   import org.codehaus.groovy.reflection.CachedMethod;
   27   import org.codehaus.groovy.reflection.ClassInfo;
   28   import org.codehaus.groovy.reflection.GeneratedMetaMethod;
   29   import org.codehaus.groovy.reflection.ParameterTypes;
   30   import org.codehaus.groovy.reflection.ReflectionCache;
   31   import org.codehaus.groovy.runtime.ConvertedClosure;
   32   import org.codehaus.groovy.runtime.CurriedClosure;
   33   import org.codehaus.groovy.runtime.DefaultGroovyMethods;
   34   import org.codehaus.groovy.runtime.GroovyCategorySupport;
   35   import org.codehaus.groovy.runtime.InvokerHelper;
   36   import org.codehaus.groovy.runtime.InvokerInvocationException;
   37   import org.codehaus.groovy.runtime.MetaClassHelper;
   38   import org.codehaus.groovy.runtime.MethodClosure;
   39   import org.codehaus.groovy.runtime.callsite.CallSite;
   40   import org.codehaus.groovy.runtime.callsite.ConstructorSite;
   41   import org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite;
   42   import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
   43   import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite;
   44   import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite;
   45   import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite;
   46   import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite;
   47   import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite;
   48   import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
   49   import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
   50   import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex;
   51   import org.codehaus.groovy.runtime.metaclass.MethodSelectionException;
   52   import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
   53   import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed;
   54   import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
   55   import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
   56   import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
   57   import org.codehaus.groovy.runtime.metaclass.NewMetaMethod;
   58   import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod;
   59   import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod;
   60   import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
   61   import org.codehaus.groovy.runtime.typehandling.NumberMathModificationInfo;
   62   import org.codehaus.groovy.runtime.wrappers.Wrapper;
   63   import org.codehaus.groovy.util.ComplexKeyHashMap;
   64   import org.codehaus.groovy.util.FastArray;
   65   import org.codehaus.groovy.util.SingleKeyHashMap;
   66   import org.objectweb.asm.ClassVisitor;
   67   
   68   import java.beans.BeanInfo;
   69   import java.beans.EventSetDescriptor;
   70   import java.beans.IntrospectionException;
   71   import java.beans.Introspector;
   72   import java.beans.PropertyDescriptor;
   73   import java.lang.reflect.Constructor;
   74   import java.lang.reflect.Method;
   75   import java.lang.reflect.Modifier;
   76   import java.lang.reflect.Proxy;
   77   import java.net.URL;
   78   import java.security.AccessController;
   79   import java.security.PrivilegedAction;
   80   import java.security.PrivilegedActionException;
   81   import java.security.PrivilegedExceptionAction;
   82   import java.util.ArrayList;
   83   import java.util.Arrays;
   84   import java.util.Collection;
   85   import java.util.Collections;
   86   import java.util.Comparator;
   87   import java.util.HashMap;
   88   import java.util.HashSet;
   89   import java.util.Iterator;
   90   import java.util.LinkedList;
   91   import java.util.List;
   92   import java.util.Map;
   93   import java.util.Set;
   94   
   95   /**
   96    * Allows methods to be dynamically added to existing classes at runtime
   97    *
   98    * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
   99    * @author Guillaume Laforge
  100    * @author Jochen Theodorou
  101    * @author Graeme Rocher
  102    * @author Alex Tkachman
  103    * @version $Revision: 17456 $
  104    * @see groovy.lang.MetaClass
  105    */
  106   public class MetaClassImpl implements MetaClass, MutableMetaClass {
  107   
  108       private static final String CLOSURE_CALL_METHOD = "call";
  109       private static final String CLOSURE_DO_CALL_METHOD = "doCall";
  110       private static final String CLOSURE_CURRY_METHOD = "curry";
  111       protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
  112       protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
  113       protected static final String METHOD_MISSING = "methodMissing";
  114       protected static final String PROPERTY_MISSING = "propertyMissing";
  115       private static final String GET_PROPERTY_METHOD = "getProperty";
  116       private static final String SET_PROPERTY_METHOD = "setProperty";
  117       protected static final String INVOKE_METHOD_METHOD = "invokeMethod";
  118   
  119       private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class};
  120       private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class};
  121       private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
  122   
  123       protected final Class theClass;
  124       protected final CachedClass theCachedClass;
  125       private static final MetaMethod[] EMPTY = new MetaMethod[0];
  126       protected MetaMethod getPropertyMethod;
  127       protected MetaMethod invokeMethodMethod;
  128       protected MetaMethod setPropertyMethod;
  129   
  130       public final CachedClass getTheCachedClass() {
  131           return theCachedClass;
  132       }
  133   
  134       protected MetaClassRegistry registry;
  135       protected final boolean isGroovyObject;
  136       protected final boolean isMap;
  137       private ClassNode classNode;
  138   
  139       private final Index classPropertyIndex = new MethodIndex();
  140       private Index classPropertyIndexForSuper = new MethodIndex();
  141       private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
  142   
  143       private final Map<String, MetaMethod> listeners = new HashMap<String, MetaMethod>();
  144       private FastArray constructors;
  145       private final List<MetaMethod> allMethods = new ArrayList<MetaMethod>();
  146       private boolean initialized;
  147       // we only need one of these that can be reused over and over.
  148       private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
  149       private static final MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod();
  150       private static final Object[] EMPTY_ARGUMENTS = {};
  151       private final Set<MetaMethod> newGroovyMethodsSet = new HashSet<MetaMethod>();
  152   
  153       private MetaMethod genericGetMethod;
  154       private MetaMethod genericSetMethod;
  155       private MetaMethod propertyMissingGet;
  156       private MetaMethod propertyMissingSet;
  157       private MetaMethod methodMissing;
  158       private MetaMethodIndex.Header mainClassMethodHeader;
  159       protected final MetaMethodIndex metaMethodIndex;
  160   
  161       private final MetaMethod [] myNewMetaMethods;
  162       private final MetaMethod [] additionalMetaMethods;
  163   
  164       public MetaClassImpl(final Class theClass, MetaMethod [] add) {
  165           this.theClass = theClass;
  166           theCachedClass = ReflectionCache.getCachedClass(theClass);
  167           this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
  168           this.isMap = Map.class.isAssignableFrom(theClass);
  169           this.registry = GroovySystem.getMetaClassRegistry();
  170           metaMethodIndex = new MetaMethodIndex(theCachedClass);
  171           final MetaMethod[] metaMethods = theCachedClass.getNewMetaMethods();
  172           if (add != null && !(add.length == 0)) {
  173               ArrayList<MetaMethod> arr = new ArrayList<MetaMethod>();
  174               arr.addAll(Arrays.asList(metaMethods));
  175               arr.addAll(Arrays.asList(add));
  176               myNewMetaMethods = arr.toArray(new MetaMethod[arr.size()]);
  177               additionalMetaMethods = metaMethods;
  178           }
  179           else {
  180               myNewMetaMethods = metaMethods;
  181               additionalMetaMethods = EMPTY;
  182           }
  183       }
  184   
  185       public MetaClassImpl(final Class theClass) {
  186           this(theClass, null);
  187       }
  188   
  189       public MetaClassImpl(MetaClassRegistry registry, final Class theClass, MetaMethod add []) {
  190           this(theClass, add);
  191           this.registry = registry;
  192           this.constructors = new FastArray(theCachedClass.getConstructors());
  193       }
  194   
  195       public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
  196           this(registry, theClass, null);
  197       }
  198   
  199       /**
  200        * @see MetaObjectProtocol#respondsTo(Object, String, Object[])
  201        */
  202       public List respondsTo(Object obj, String name, Object[] argTypes) {
  203           Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
  204           MetaMethod m = getMetaMethod(name, classes);
  205           List<MetaMethod> methods = new ArrayList<MetaMethod>();
  206           if (m != null) {
  207               methods.add(m);
  208           }
  209           return methods;
  210       }
  211   
  212       /**
  213        * @see MetaObjectProtocol#respondsTo(Object, String)
  214        */
  215       public List respondsTo(final Object obj, final String name) {
  216           final Object o = getMethods(getTheClass(), name, false);
  217           if (o instanceof FastArray)
  218               return ((FastArray) o).toList();
  219           else
  220               return Collections.singletonList(o);
  221       }
  222   
  223       /**
  224        * @see MetaObjectProtocol#hasProperty(Object,String)
  225        */
  226       public MetaProperty hasProperty(Object obj, String name) {
  227           return getMetaProperty(name);
  228       }
  229   
  230       /**
  231        * @see MetaObjectProtocol#getMetaProperty(String)
  232        */
  233       public MetaProperty getMetaProperty(String name) {
  234           SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
  235           if (propertyMap.containsKey(name)) {
  236               return (MetaProperty) propertyMap.get(name);
  237           } else if (staticPropertyIndex.containsKey(name)) {
  238               return (MetaProperty) staticPropertyIndex.get(name);
  239           } else {
  240               propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
  241               if (propertyMap.containsKey(name))
  242                   return (MetaProperty) propertyMap.get(name);
  243               else {
  244                   CachedClass superClass = theCachedClass;
  245                   while (superClass != null && superClass != ReflectionCache.OBJECT_CLASS) {
  246                       final MetaBeanProperty property = findPropertyInClassHierarchy(name, superClass);
  247                       if (property != null) {
  248                           onSuperPropertyFoundInHierarchy(property);
  249                           return property;
  250                       }
  251                       superClass = superClass.getCachedSuperClass();
  252                   }
  253                   return null;
  254               }
  255           }
  256       }
  257   
  258       /**
  259        * @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
  260        */
  261       public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) {
  262           Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
  263           return pickStaticMethod(name, classes);
  264       }
  265   
  266   
  267       /**
  268        * @see MetaObjectProtocol#getMetaMethod(String, Object[])
  269        */
  270       public MetaMethod getMetaMethod(String name, Object[] argTypes) {
  271           Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
  272           return pickMethod(name, classes);
  273       }
  274   
  275       public Class getTheClass() {
  276           return this.theClass;
  277       }
  278   
  279       public boolean isGroovyObject() {
  280           return isGroovyObject;
  281       }
  282   
  283       private void fillMethodIndex() {
  284           mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
  285           LinkedList superClasses = getSuperClasses();
  286           CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
  287   
  288           Set<CachedClass> interfaces = theCachedClass.getInterfaces();
  289           addInterfaceMethods(interfaces);
  290   
  291           populateMethods(superClasses, firstGroovySuper);
  292   
  293           inheritInterfaceNewMetaMethods(interfaces);
  294           if (isGroovyObject) {
  295             metaMethodIndex.copyMethodsToSuper();
  296   
  297             connectMultimethods(superClasses, firstGroovySuper);
  298             removeMultimethodsOverloadedWithPrivateMethods();
  299   
  300             replaceWithMOPCalls(theCachedClass.mopMethods);
  301           }
  302       }
  303   
  304       private void populateMethods(LinkedList superClasses, CachedClass firstGroovySuper) {
  305           Iterator iter = superClasses.iterator();
  306   
  307           MetaMethodIndex.Header header = metaMethodIndex.getHeader(firstGroovySuper.getTheClass());
  308           CachedClass c;
  309           for (; iter.hasNext();) {
  310               c = (CachedClass) iter.next();
  311   
  312               CachedMethod[] cachedMethods = c.getMethods();
  313               for (CachedMethod metaMethod : cachedMethods) {
  314                   addToAllMethodsIfPublic(metaMethod);
  315                   if (!metaMethod.isPrivate() || c == firstGroovySuper)
  316                       addMetaMethodToIndex(metaMethod, header);
  317               }
  318   
  319               MetaMethod[] cachedMethods1 = getNewMetaMethods(c);
  320               for (final MetaMethod method : cachedMethods1) {
  321                   if (!newGroovyMethodsSet.contains(method)) {
  322                       newGroovyMethodsSet.add(method);
  323                       addMetaMethodToIndex(method, header);
  324                   }
  325               }
  326   
  327               if (c == firstGroovySuper)
  328                 break;
  329           }
  330   
  331           MetaMethodIndex.Header last = header;
  332           for (;iter.hasNext();) {
  333               c = (CachedClass) iter.next();
  334               header = metaMethodIndex.getHeader(c.getTheClass());
  335   
  336               if (last != null) {
  337                   metaMethodIndex.copyNonPrivateMethods(last, header);
  338               }
  339               last = header;
  340   
  341               for (CachedMethod metaMethod : c.getMethods()) {
  342                   addToAllMethodsIfPublic(metaMethod);
  343                   addMetaMethodToIndex(metaMethod, header);
  344               }
  345   
  346               for (final MetaMethod method : getNewMetaMethods(c)) {
  347                   if (!newGroovyMethodsSet.contains(method)) {
  348                       newGroovyMethodsSet.add(method);
  349                       addMetaMethodToIndex(method, header);
  350                   }
  351               }
  352           }
  353       }
  354   
  355       private MetaMethod[] getNewMetaMethods(CachedClass c) {
  356           if (theCachedClass != c)
  357             return c.getNewMetaMethods();
  358   
  359           return myNewMetaMethods;
  360       }
  361   
  362       private void addInterfaceMethods(Set<CachedClass> interfaces) {
  363           MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass);
  364           for (CachedClass c : interfaces) {
  365               final CachedMethod[] m = c.getMethods();
  366               for (int i = 0; i != m.length; ++i) {
  367                   MetaMethod method = m[i];
  368                   addMetaMethodToIndex(method, header);
  369               }
  370           }
  371       }
  372   
  373       protected LinkedList<CachedClass> getSuperClasses() {
  374           LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>();
  375   
  376           if (theClass.isInterface()) {
  377               superClasses.addFirst(ReflectionCache.OBJECT_CLASS);
  378           } else {
  379               for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
  380                   superClasses.addFirst(c);
  381               }
  382               if (theCachedClass.isArray && theClass != Object[].class && !theClass.getComponentType().isPrimitive()) {
  383                   superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS);
  384               }
  385           }
  386           return superClasses;
  387       }
  388   
  389       private void removeMultimethodsOverloadedWithPrivateMethods() {
  390           MethodIndexAction mia = new MethodIndexAction() {
  391               public boolean skipClass(Class clazz) {
  392                   return clazz == theClass;
  393               }
  394   
  395               public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
  396                   if (e.methods == null)
  397                     return;
  398   
  399                   boolean hasPrivate = false;
  400                   if (e.methods instanceof FastArray) {
  401                       FastArray methods = (FastArray) e.methods;
  402                       final int len = methods.size();
  403                       final Object[] data = methods.getArray();
  404                       for (int i = 0; i != len; ++i) {
  405                           MetaMethod method = (MetaMethod) data[i];
  406                           if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
  407                               hasPrivate = true;
  408                               break;
  409                           }
  410                       }
  411                   }
  412                   else {
  413                       MetaMethod method = (MetaMethod) e.methods;
  414                       if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
  415                          hasPrivate = true;
  416                       }
  417                   }
  418   
  419                   if (!hasPrivate) return;
  420   
  421                   // We have private methods for that name, so remove the
  422                   // multimethods. That is the same as in our index for
  423                   // super, so just copy the list from there. It is not
  424                   // possible to use a pointer here, because the methods
  425                   // in the index for super are replaced later by MOP
  426                   // methods like super$5$foo
  427                   final Object o = e.methodsForSuper;
  428                   if (o instanceof FastArray)
  429                     e.methods = ((FastArray) o).copy();
  430                   else
  431                     e.methods = o;
  432               }
  433           };
  434           mia.iterate();
  435       }
  436   
  437   
  438       private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
  439           // no MOP methods if not a child of GroovyObject
  440           if (!isGroovyObject) return;
  441   
  442           class MOPIter extends MethodIndexAction {
  443               boolean useThis;
  444               public boolean skipClass(CachedClass clazz) {
  445                   return !useThis && clazz == theCachedClass;
  446               }
  447   
  448               public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
  449                   if (useThis) {
  450                       if (e.methods == null)
  451                         return;
  452   
  453                       if (e.methods instanceof FastArray) {
  454                           FastArray methods = (FastArray) e.methods;
  455                           processFastArray(methods);
  456                       }
  457                       else {
  458                           MetaMethod method = (MetaMethod) e.methods;
  459                           if (method instanceof NewMetaMethod)
  460                             return;
  461                           if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
  462                             return;
  463                           String mopName = method.getMopName();
  464                           int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
  465                           if (index >= 0) {
  466                               int from = index;
  467                               while (from > 0 && mopMethods[from-1].getName().equals(mopName))
  468                                 from--;
  469                               int to = index;
  470                               while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
  471                                 to++;
  472   
  473                               int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
  474                               if (matchingMethod != -1) {
  475                                   e.methods = mopMethods[matchingMethod];
  476                               }
  477                           }
  478                       }
  479                   }
  480                   else {
  481                       if (e.methodsForSuper == null)
  482                         return;
  483   
  484                       if (e.methodsForSuper instanceof FastArray) {
  485                           FastArray methods = (FastArray) e.methodsForSuper;
  486                           processFastArray(methods);
  487                       }
  488                       else {
  489                           MetaMethod method = (MetaMethod) e.methodsForSuper;
  490                           if (method instanceof NewMetaMethod)
  491                             return;
  492                           if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
  493                             return;
  494                           String mopName = method.getMopName();
  495                           int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
  496                           if (index >= 0) {
  497                               int from = index;
  498                               while (from > 0 && mopMethods[from-1].getName().equals(mopName))
  499                                 from--;
  500                               int to = index;
  501                               while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
  502                                 to++;
  503   
  504                               int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
  505                               if (matchingMethod != -1) {
  506                                   e.methodsForSuper = mopMethods[matchingMethod];
  507                               }
  508                           }
  509                       }
  510                   }
  511               }
  512   
  513               private void processFastArray(FastArray methods) {
  514                   final int len = methods.size();
  515                   final Object[] data = methods.getArray();
  516                   for (int i = 0; i != len; ++i) {
  517                       MetaMethod method = (MetaMethod) data[i];
  518                       if (method instanceof NewMetaMethod)
  519                         continue;
  520                       if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) continue;
  521                       String mopName = method.getMopName();
  522                       int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
  523                       if (index >= 0) {
  524                           int from = index;
  525                           while (from > 0 && mopMethods[from-1].getName().equals(mopName))
  526                             from--;
  527                           int to = index;
  528                           while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
  529                             to++;
  530   
  531                           int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
  532                           if (matchingMethod != -1) {
  533                               methods.set(i, mopMethods[matchingMethod]);
  534                           }
  535                       }
  536                   }
  537               }
  538           }
  539           MOPIter iter = new MOPIter();
  540   
  541           // replace all calls for super with the correct MOP method
  542           iter.useThis = false;
  543           iter.iterate();
  544           // replace all calls for this with the correct MOP method
  545           iter.useThis = true;
  546           iter.iterate();
  547       }
  548   
  549       private void inheritInterfaceNewMetaMethods(Set<CachedClass> interfaces) {
  550           // add methods declared by DGM for interfaces
  551           for (CachedClass cls : interfaces) {
  552               MetaMethod methods[] = getNewMetaMethods(cls);
  553               for (MetaMethod method : methods) {
  554                   if (!newGroovyMethodsSet.contains(method)) {
  555                       newGroovyMethodsSet.add(method);
  556                   }
  557                   addMetaMethodToIndex(method, mainClassMethodHeader);
  558               }
  559           }
  560       }
  561   
  562       private void connectMultimethods(List superClasses, CachedClass firstGroovyClass) {
  563           superClasses = DefaultGroovyMethods.reverse(superClasses);
  564           MetaMethodIndex.Header last = null;
  565           for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
  566               CachedClass c = (CachedClass) iter.next();
  567               MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getTheClass());
  568               // We don't copy DGM methods to superclasses' indexes
  569               // The reason we can do that is particular set of DGM methods in use,
  570               // if at some point we will define DGM method for some Groovy class or
  571               // for a class derived from such, we will need to revise this condition.
  572               // It saves us a lot of space and some noticeable time
  573               if (last != null) metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex);
  574               last = methodIndex;
  575   
  576               if (c == firstGroovyClass)
  577                 break;
  578           }
  579       }
  580   
  581       private CachedClass calcFirstGroovySuperClass(Collection superClasses) {
  582           if (theCachedClass.isInterface)
  583             return ReflectionCache.OBJECT_CLASS;
  584   
  585           CachedClass firstGroovy = null;
  586           Iterator iter = superClasses.iterator();
  587           for (; iter.hasNext();) {
  588               CachedClass c = (CachedClass) iter.next();
  589               if (GroovyObject.class.isAssignableFrom(c.getTheClass())) {
  590                 firstGroovy = c;
  591                 break;
  592               }
  593           }
  594   
  595           if (firstGroovy == null)
  596             firstGroovy = theCachedClass;
  597           else {
  598               if (firstGroovy.getTheClass() == GroovyObjectSupport.class && iter.hasNext()) {
  599                   firstGroovy = (CachedClass) iter.next();
  600                   if (firstGroovy.getTheClass() == Closure.class && iter.hasNext()) {
  601                       firstGroovy = (CachedClass) iter.next();
  602                   }
  603               }
  604           }
  605   
  606           return GroovyObject.class.isAssignableFrom(firstGroovy.getTheClass()) ? firstGroovy.getCachedSuperClass() : firstGroovy;
  607       }
  608   
  609       /**
  610        * @return all the normal instance methods avaiable on this class for the
  611        *         given name
  612        */
  613       private Object getMethods(Class sender, String name, boolean isCallToSuper) {
  614           Object answer;
  615   
  616           final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
  617           if (entry == null)
  618               answer = FastArray.EMPTY_LIST;
  619           else
  620               if (isCallToSuper) {
  621                   answer = entry.methodsForSuper;
  622               } else {
  623                   answer = entry.methods;
  624               }
  625   
  626           if (answer == null) answer = FastArray.EMPTY_LIST;
  627   
  628           if (!isCallToSuper) {
  629               List used = GroovyCategorySupport.getCategoryMethods(name);
  630               if (used != null) {
  631                   FastArray arr;
  632                   if (answer instanceof MetaMethod) {
  633                       arr = new FastArray();
  634                       arr.add(answer);
  635                   }
  636                   else
  637                       arr = ((FastArray) answer).copy();
  638   
  639                   for (Iterator iter = used.iterator(); iter.hasNext();) {
  640                       MetaMethod element = (MetaMethod) iter.next();
  641                       if (!element.getDeclaringClass().getTheClass().isAssignableFrom(sender))
  642                         continue;
  643                       filterMatchingMethodForCategory(arr, element);
  644                   }
  645                   answer = arr;
  646               }
  647           }
  648           return answer;
  649       }
  650   
  651       /**
  652        * @return all the normal static methods available on this class for the
  653        *         given name
  654        */
  655       private Object getStaticMethods(Class sender, String name) {
  656           final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
  657           if (entry == null)
  658               return FastArray.EMPTY_LIST;
  659           Object answer = entry.staticMethods;
  660           if (answer == null)
  661               return FastArray.EMPTY_LIST;
  662           return answer;
  663       }
  664   
  665       public boolean isModified() {
  666           return false;  // MetaClassImpl not designed for modification, just return false
  667       }
  668   
  669       public void addNewInstanceMethod(Method method) {
  670           final CachedMethod cachedMethod = CachedMethod.find(method);
  671           NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod);
  672           final CachedClass declaringClass = newMethod.getDeclaringClass();
  673           addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass()));
  674       }
  675   
  676       private void addNewInstanceMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
  677           if (!newGroovyMethodsSet.contains(newMethod)) {
  678               newGroovyMethodsSet.add(newMethod);
  679               addMetaMethodToIndex(newMethod, header);
  680           }
  681       }
  682   
  683       public void addNewStaticMethod(Method method) {
  684           final CachedMethod cachedMethod = CachedMethod.find(method);
  685           NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
  686           final CachedClass declaringClass = newMethod.getDeclaringClass();
  687           addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass()));
  688       }
  689   
  690       private void addNewStaticMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
  691           if (!newGroovyMethodsSet.contains(newMethod)) {
  692               newGroovyMethodsSet.add(newMethod);
  693               addMetaMethodToIndex(newMethod, header);
  694           }
  695       }
  696   
  697       public Object invokeMethod(Object object, String methodName, Object arguments) {
  698           if (arguments == null) {
  699               return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
  700           }
  701           if (arguments instanceof Tuple) {
  702               Tuple tuple = (Tuple) arguments;
  703               return invokeMethod(object, methodName, tuple.toArray());
  704           }
  705           if (arguments instanceof Object[]) {
  706               return invokeMethod(object, methodName, (Object[]) arguments);
  707           } else {
  708               return invokeMethod(object, methodName, new Object[]{arguments});
  709           }
  710       }
  711   
  712       public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
  713           return invokeMissingMethod(instance, methodName, arguments, null, false);
  714       }
  715   
  716       public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
  717           Class theClass = instance instanceof Class ? (Class)instance : instance.getClass();
  718           CachedClass superClass = theCachedClass;
  719           while(superClass != null && superClass != ReflectionCache.OBJECT_CLASS) {
  720               final MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, superClass);
  721               if(property != null) {
  722                   onSuperPropertyFoundInHierarchy(property);
  723                   if(!isGetter) {
  724                       property.setProperty(instance, optionalValue);
  725                       return null;
  726                   }
  727                   else {
  728                       return property.getProperty(instance);
  729                   }
  730               }
  731               superClass = superClass.getCachedSuperClass();
  732           }
  733           // got here to property not found, look for getProperty or setProperty overrides
  734           if(isGetter) {
  735               final Class[] getPropertyArgs = {String.class};
  736               final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD, getPropertyArgs, this);
  737               if(method != null && method instanceof ClosureMetaMethod) {
  738                   onGetPropertyFoundInHierarchy(method);
  739                   return method.invoke(instance,new Object[]{propertyName});
  740               }
  741           }
  742           else {
  743               final Class[] setPropertyArgs = {String.class, Object.class};
  744               final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD, setPropertyArgs, this);
  745               if(method != null && method instanceof ClosureMetaMethod) {
  746                   onSetPropertyFoundInHierarchy(method);
  747                   return method.invoke(instance, new Object[]{propertyName, optionalValue});
  748               }
  749           }
  750   
  751           try {
  752               if (!(instance instanceof Class)) {
  753                   if (isGetter && propertyMissingGet != null) {
  754                       return propertyMissingGet.invoke(instance, new Object[]{propertyName});
  755                   } else {
  756                       if (propertyMissingSet != null)
  757                           return propertyMissingSet.invoke(instance, new Object[]{propertyName, optionalValue});
  758                   }
  759               }
  760           } catch (InvokerInvocationException iie) {
  761               boolean shouldHandle = isGetter && propertyMissingGet != null;
  762               if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet != null;
  763               if (shouldHandle &&  iie.getCause() instanceof MissingPropertyException) {
  764                   throw (MissingPropertyException) iie.getCause();
  765               }
  766               throw iie;
  767           }
  768   
  769           if (instance instanceof Class && theClass != Class.class) {
  770              final MetaProperty metaProperty = InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName);
  771              if (metaProperty != null)
  772                if (isGetter)
  773                  return metaProperty.getProperty(instance);
  774                else {
  775                  metaProperty.setProperty(instance, optionalValue);
  776                  return null;
  777                }
  778           }
  779           throw new MissingPropertyExceptionNoStack(propertyName, theClass);
  780       }
  781   
  782       private Object invokeMissingMethod(Object instance, String methodName, Object[] arguments, RuntimeException original, boolean isCallToSuper) {
  783           if (!isCallToSuper) {
  784               Class instanceKlazz = instance.getClass();
  785               if (theClass != instanceKlazz && theClass.isAssignableFrom(instanceKlazz))
  786                 instanceKlazz = theClass;
  787               
  788               Class[] argClasses = MetaClassHelper.castArgumentsToClassArray(arguments);
  789   
  790               MetaMethod method = findMixinMethod(methodName, argClasses);
  791               if(method != null) {
  792                   onMixinMethodFound(method);
  793                   return method.invoke(instance, arguments);
  794               }
  795   
  796               method = findMethodInClassHierarchy(instanceKlazz, methodName, argClasses, this);
  797               if(method != null) {
  798                   onSuperMethodFoundInHierarchy(method);
  799                   return method.invoke(instance, arguments);
  800               }
  801   
  802               // still not method here, so see if there is an invokeMethod method up the hierarchy
  803               final Class[] invokeMethodArgs = {String.class, Object[].class};
  804               method = findMethodInClassHierarchy(instanceKlazz, INVOKE_METHOD_METHOD, invokeMethodArgs, this );
  805               if(method != null && method instanceof ClosureMetaMethod) {
  806                   onInvokeMethodFoundInHierarchy(method);
  807                   return method.invoke(instance, invokeMethodArgs);
  808               }
  809           }
  810   
  811           if (methodMissing != null) {
  812               try {
  813                   return methodMissing.invoke(instance, new Object[]{methodName, arguments});
  814               } catch (InvokerInvocationException iie) {
  815                   if (methodMissing instanceof ClosureMetaMethod && iie.getCause() instanceof MissingMethodException) {
  816                       MissingMethodException mme =  (MissingMethodException) iie.getCause();
  817                       throw new MissingMethodExecutionFailed (mme.getMethod(), mme.getClass(),
  818                                                               mme.getArguments(),mme.isStatic(),mme);
  819                   }
  820                   throw iie;
  821               }
  822           } else if (original != null) throw original;
  823           else throw new MissingMethodExceptionNoStack(methodName, theClass, arguments, false);
  824       }
  825   
  826       protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) {
  827       }
  828   
  829       protected void onMixinMethodFound(MetaMethod method) {
  830       }
  831   
  832       protected void onSuperMethodFoundInHierarchy(MetaMethod method) {
  833       }
  834   
  835       protected void onInvokeMethodFoundInHierarchy(MetaMethod method) {
  836       }
  837   
  838       protected void onSetPropertyFoundInHierarchy(MetaMethod method) {
  839       }
  840   
  841       protected void onGetPropertyFoundInHierarchy(MetaMethod method) {
  842       }
  843   
  844   
  845       /**
  846        * Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up
  847        * "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException
  848        *
  849        * @param instance      The instance
  850        * @param propertyName  The name of the property
  851        * @param optionalValue The value in the case of a setter
  852        * @param isGetter      True if its a getter
  853        * @return The value in the case of a getter or a MissingPropertyException
  854        */
  855       protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
  856           MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
  857           if (isGetter) {
  858               MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
  859               if (propertyMissing != null) {
  860                   return propertyMissing.invoke(instance, new Object[]{propertyName});
  861               }
  862           } else {
  863               MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
  864               if (propertyMissing != null) {
  865                   return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
  866               }
  867           }
  868   
  869           if (instance instanceof Class) {
  870               throw new MissingPropertyException(propertyName, (Class) instance);
  871           }
  872           throw new MissingPropertyException(propertyName, theClass);
  873       }
  874   
  875       /**
  876        * Invokes the given method on the object.
  877        * TODO: should this be deprecated? If so, we have to propogate to many places.
  878        */
  879       public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
  880           return invokeMethod(theClass, object, methodName, originalArguments, false, false);
  881       }
  882   
  883   
  884       /**
  885        * Invokes the given method on the object.
  886        */
  887       public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
  888           checkInitalised();
  889           if (object == null) {
  890               throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
  891           }
  892   
  893           final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
  894   //        final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
  895   //
  896   //        unwrap(arguments);
  897   
  898           MetaMethod method = getMethodWithCaching(sender, methodName, arguments, isCallToSuper);
  899           MetaClassHelper.unwrap(arguments);
  900   
  901           if (method == null)
  902               method = tryListParamMetaMethod(sender, methodName, isCallToSuper, arguments);
  903   
  904           final boolean isClosure = object instanceof Closure;
  905           if (isClosure) {
  906               final Closure closure = (Closure) object;
  907   
  908               final Object owner = closure.getOwner();
  909   
  910               if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) {
  911                   final Class objectClass = object.getClass();
  912                   if (objectClass == MethodClosure.class) {
  913                       final MethodClosure mc = (MethodClosure) object;
  914                       methodName = mc.getMethod();
  915                       final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
  916                       final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
  917                       return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, arguments, false, false);
  918                   } else if (objectClass == CurriedClosure.class) {
  919                       final CurriedClosure cc = (CurriedClosure) object;
  920                       // change the arguments for an uncurried call
  921                       final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
  922                       final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
  923                       final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
  924                       return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
  925                   }
  926                   if (method==null) invokeMissingMethod(object,methodName,arguments);
  927               } else if (CLOSURE_CURRY_METHOD.equals(methodName)) {
  928                   return closure.curry(arguments);
  929               }
  930   
  931               final Object delegate = closure.getDelegate();
  932               final boolean isClosureNotOwner = owner != closure;
  933               final int resolveStrategy = closure.getResolveStrategy();
  934   
  935               final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
  936   
  937               switch (resolveStrategy) {
  938                   case Closure.TO_SELF:
  939                       method = closure.getMetaClass().pickMethod(methodName, argClasses);
  940                       if (method != null) return method.invoke(closure, arguments);
  941                       break;
  942                   case Closure.DELEGATE_ONLY:
  943                       if (method == null && delegate != closure && delegate != null) {
  944                           MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
  945                           method = delegateMetaClass.pickMethod(methodName, argClasses);
  946                           if (method != null)
  947                               return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
  948                           else if (delegate != closure && (delegate instanceof GroovyObject)) {
  949                               return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
  950                           }
  951                       }
  952                       break;
  953                   case Closure.OWNER_ONLY:
  954                       if (method == null && owner != closure) {
  955                           MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
  956                           return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
  957                       }
  958                       break;
  959                   case Closure.DELEGATE_FIRST:
  960                       if (method == null && delegate != closure && delegate != null) {
  961                           MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
  962                           method = delegateMetaClass.pickMethod(methodName, argClasses);
  963                           if (method != null)
  964                               return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
  965                       }
  966                       if (method == null && owner != closure) {
  967                           MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
  968                           method = ownerMetaClass.pickMethod(methodName, argClasses);
  969                           if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
  970                       }
  971                       if (method == null && resolveStrategy != Closure.TO_SELF) {
  972                           // still no methods found, test if delegate or owner are GroovyObjects
  973                           // and invoke the method on them if so.
  974                           MissingMethodException last = null;
  975                           if (delegate != closure && (delegate instanceof GroovyObject)) {
  976                               try {
  977                                   return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
  978                               } catch (MissingMethodException mme) {
  979                                   if (last == null) last = mme;
  980                               }
  981                           }
  982                           if (isClosureNotOwner && (owner instanceof GroovyObject)) {
  983                               try {
  984                                   return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
  985                               } catch (MissingMethodException mme) {
  986                                   last = mme;
  987                               }
  988                           }
  989                           if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper);
  990                       }
  991   
  992                       break;
  993                   default:
  994                       if (method == null && owner != closure) {
  995                           MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
  996                           method = ownerMetaClass.pickMethod(methodName, argClasses);
  997                           if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
  998                       }
  999                       if (method == null && delegate != closure && delegate != null) {
 1000                           MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
 1001                           method = delegateMetaClass.pickMethod(methodName, argClasses);
 1002                           if (method != null)
 1003                               return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
 1004                       }
 1005                       if (method == null && resolveStrategy != Closure.TO_SELF) {
 1006                           // still no methods found, test if delegate or owner are GroovyObjects
 1007                           // and invoke the method on them if so.
 1008                           MissingMethodException last = null;
 1009                           if (isClosureNotOwner && (owner instanceof GroovyObject)) {
 1010                               try {
 1011                                   return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
 1012                               } catch (MissingMethodException mme) {
 1013                                   // proboboly needed here, but we need a test case to trip it first
 1014                                   if (last == null) last = mme;
 1015                               }
 1016                               catch (InvokerInvocationException iie) {
 1017                                   if (iie.getCause() instanceof MissingMethodException) {
 1018                                       MissingMethodException mme = (MissingMethodException) iie.getCause();
 1019                                       if (methodName.equals(mme.getMethod())) {
 1020                                           if (last == null) last = mme;
 1021                                       } else {
 1022                                           throw iie;
 1023                                       }
 1024                                   }
 1025                                   else
 1026                                     throw iie;
 1027                               }
 1028                           }
 1029                           if (delegate != closure && (delegate instanceof GroovyObject)) {
 1030                               try {
 1031                                   return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
 1032                               } catch (MissingMethodException mme) {
 1033                                   last = mme;
 1034                               }
 1035                               catch (InvokerInvocationException iie) {
 1036                                   if (iie.getCause() instanceof MissingMethodException) {
 1037                                       last = (MissingMethodException) iie.getCause();
 1038                                   }
 1039                                   else
 1040                                     throw iie;
 1041                               }
 1042                           }
 1043                           if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper);
 1044                       }
 1045               }
 1046           }
 1047   
 1048           if (method != null) {
 1049               return method.doMethodInvoke(object, arguments);
 1050           } else {
 1051               return invokePropertyOrMissing(object, methodName, originalArguments, fromInsideClass, isCallToSuper);
 1052           }
 1053       }
 1054   
 1055       private MetaMethod tryListParamMetaMethod(Class sender, String methodName, boolean isCallToSuper, Object[] arguments) {
 1056           MetaMethod method = null;
 1057           if (arguments.length == 1 && arguments[0] instanceof List) {
 1058               Object[] newArguments = ((List) arguments[0]).toArray();
 1059               method = getMethodWithCaching(sender, methodName, newArguments, isCallToSuper);
 1060               if (method != null) {
 1061                   method = new TransformMetaMethod(method) {
 1062                       public Object invoke(Object object, Object[] arguments) {
 1063                           Object firstArgument = arguments[0];
 1064                           List list = (List) firstArgument;
 1065                           arguments = list.toArray();
 1066                           return super.invoke(object, arguments);
 1067                       }
 1068                   };
 1069               }
 1070           }
 1071           return method;
 1072       }
 1073   
 1074       private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
 1075           // if no method was found, try to find a closure defined as a field of the class and run it
 1076           Object value = null;
 1077           final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
 1078           if (metaProperty != null)
 1079             value = metaProperty.getProperty(object);
 1080           else {
 1081               if (object instanceof Map)
 1082                 value = ((Map)object).get(methodName);
 1083           }
 1084   
 1085           if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
 1086               Closure closure = (Closure) value;
 1087               MetaClass delegateMetaClass = closure.getMetaClass();
 1088               return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
 1089           }
 1090   
 1091           if(object instanceof Script) {
 1092           	Object bindingVar = ((Script)object).getBinding().getVariables().get(methodName);
 1093           	if(bindingVar != null) {
 1094           		MetaClass bindingVarMC = ((MetaClassRegistryImpl)registry).getMetaClass(bindingVar);
 1095           		return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
 1096           	}
 1097           }
 1098           return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
 1099       }
 1100   
 1101       private MetaClass lookupObjectMetaClass(Object object) {
 1102           if (object instanceof GroovyObject) {
 1103               GroovyObject go = (GroovyObject) object;
 1104               return go.getMetaClass();
 1105           }
 1106           Class ownerClass = object.getClass();
 1107           if (ownerClass == Class.class) ownerClass = (Class) object;
 1108           MetaClass metaClass = registry.getMetaClass(ownerClass);
 1109           return metaClass;
 1110       }
 1111   
 1112       private Object invokeMethodOnGroovyObject(String methodName, Object[] originalArguments, Object owner) {
 1113           GroovyObject go = (GroovyObject) owner;
 1114           return go.invokeMethod(methodName, originalArguments);
 1115       }
 1116   
 1117       public MetaMethod getMethodWithCaching(Class sender, String methodName, Object[] arguments, boolean isCallToSuper) {
 1118           // lets try use the cache to find the method
 1119           if (!isCallToSuper && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 1120               return getMethodWithoutCaching(sender, methodName, MetaClassHelper.convertToTypeArray(arguments), isCallToSuper);
 1121           } else {
 1122               final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, methodName);
 1123               if (e == null)
 1124                 return null;
 1125   
 1126               return isCallToSuper ? getSuperMethodWithCaching(arguments, e) : getNormalMethodWithCaching(arguments, e);
 1127           }
 1128       }
 1129   
 1130       private static boolean sameClasses(Class[] params, Class[] arguments, boolean weakNullCheck) {
 1131           // we do here a null check because the params field might not have been
 1132     	 	// set yet
 1133     	 	if (params==null) return false;
 1134           
 1135           if (params.length != arguments.length)
 1136             return false;
 1137   
 1138           for (int i = params.length-1; i >= 0; i--) {
 1139               Object arg = arguments[i];
 1140               if (arg != null) {
 1141                  if (params[i] != arguments[i])
 1142                     return false;
 1143               }
 1144               else
 1145                 if (!weakNullCheck)
 1146                   return false;
 1147           }
 1148   
 1149           return true;
 1150       }
 1151   
 1152       // This method should be called by CallSite only
 1153       private MetaMethod getMethodWithCachingInternal (Class sender, CallSite site, Class [] params) {
 1154           if (site.getUsage ().get() != 0 && GroovyCategorySupport.hasCategoryInCurrentThread())
 1155               return getMethodWithoutCaching(sender, site.getName (), params, false);
 1156   
 1157           final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, site.getName());
 1158           if (e == null) {
 1159               return null;
 1160           }
 1161   
 1162           MetaMethodIndex.CacheEntry cacheEntry;
 1163           final Object methods = e.methods;
 1164           if (methods == null)
 1165             return null;
 1166   
 1167           cacheEntry = e.cachedMethod;
 1168           if (cacheEntry != null
 1169              && (sameClasses(cacheEntry.params, params, methods instanceof MetaMethod))) {
 1170                return cacheEntry.method;
 1171           }
 1172   
 1173           cacheEntry = new MetaMethodIndex.CacheEntry ();
 1174           cacheEntry.params = params;
 1175           cacheEntry.method = (MetaMethod) chooseMethod(e.name, methods, params);
 1176           e.cachedMethod = cacheEntry;
 1177           return cacheEntry.method;
 1178       }
 1179   
 1180       private MetaMethod getSuperMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) {
 1181           MetaMethodIndex.CacheEntry cacheEntry;
 1182           if (e.methodsForSuper == null)
 1183             return null;
 1184   
 1185           cacheEntry = e.cachedMethodForSuper;
 1186           
 1187           if (cacheEntry != null &&
 1188               MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.methodsForSuper instanceof MetaMethod))
 1189           {
 1190               MetaMethod method = cacheEntry.method;
 1191               if (method!=null) return method;
 1192           }
 1193   
 1194           cacheEntry = new MetaMethodIndex.CacheEntry ();
 1195           final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
 1196           cacheEntry.params = classes;
 1197           cacheEntry.method = (MetaMethod) chooseMethod(e.name, e.methodsForSuper, classes);
 1198           if (cacheEntry.method.isAbstract()) cacheEntry.method = null;
 1199   
 1200           e.cachedMethodForSuper = cacheEntry;
 1201   
 1202           return cacheEntry.method;
 1203       }
 1204   
 1205       private MetaMethod getNormalMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) {
 1206           MetaMethodIndex.CacheEntry cacheEntry;
 1207           final Object methods = e.methods;
 1208           if (methods == null)
 1209             return null;
 1210   
 1211           cacheEntry = e.cachedMethod;
 1212   
 1213           if (cacheEntry != null &&
 1214               MetaClassHelper.sameClasses(cacheEntry.params, arguments, methods instanceof MetaMethod))
 1215           {
 1216               MetaMethod method = cacheEntry.method;
 1217               if (method!=null) return method;
 1218           }
 1219   
 1220           cacheEntry = new MetaMethodIndex.CacheEntry ();
 1221           final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
 1222           cacheEntry.params = classes;
 1223           cacheEntry.method = (MetaMethod) chooseMethod(e.name, methods, classes);
 1224           
 1225           e.cachedMethod = cacheEntry;
 1226           
 1227           return cacheEntry.method;
 1228       }
 1229   
 1230       public Constructor retrieveConstructor(Class[] arguments) {
 1231           CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments);
 1232           if (constructor != null) {
 1233               return constructor.cachedConstructor;
 1234           }
 1235           constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments);
 1236           if (constructor != null) {
 1237               return constructor.cachedConstructor;
 1238           }
 1239           return null;
 1240       }
 1241   
 1242       public MetaMethod retrieveStaticMethod(String methodName, Object[] arguments) {
 1243           final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(theClass, methodName);
 1244           MetaMethodIndex.CacheEntry cacheEntry;
 1245           if (e != null) {
 1246               cacheEntry = e.cachedStaticMethod;
 1247   
 1248               if (cacheEntry != null &&
 1249                   MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.staticMethods instanceof MetaMethod))
 1250               {
 1251                    return cacheEntry.method;
 1252               }
 1253   
 1254               cacheEntry = new MetaMethodIndex.CacheEntry ();
 1255               final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
 1256               cacheEntry.params = classes;
 1257               cacheEntry.method = pickStaticMethod(methodName, classes);
 1258               
 1259               e.cachedStaticMethod = cacheEntry;
 1260               
 1261               return cacheEntry.method;
 1262           }
 1263           else
 1264             return pickStaticMethod(methodName, MetaClassHelper.convertToTypeArray(arguments));
 1265       }
 1266   
 1267       public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
 1268           MetaMethod method = null;
 1269           Object methods = getMethods(sender, methodName, isCallToSuper);
 1270           if (methods != null) {
 1271               method = (MetaMethod) chooseMethod(methodName, methods, arguments);
 1272           }
 1273           return method;
 1274       }
 1275   
 1276       public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
 1277           checkInitalised();
 1278   
 1279           final Class sender = object instanceof Class ? (Class) object : object.getClass();
 1280           if (sender != theClass) {
 1281               MetaClass mc = registry.getMetaClass(sender);
 1282               return mc.invokeStaticMethod(sender, methodName, arguments);
 1283           }
 1284           if (sender == Class.class) {
 1285               return invokeMethod(object, methodName, arguments);
 1286           }
 1287   
 1288           if (arguments == null) arguments = EMPTY_ARGUMENTS;
 1289   //        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
 1290   
 1291           MetaMethod method = retrieveStaticMethod(methodName, arguments);
 1292           // lets try use the cache to find the method
 1293   
 1294           if (method != null) {
 1295               MetaClassHelper.unwrap(arguments);
 1296               return method.doMethodInvoke(object, arguments);
 1297           }
 1298           Object prop = null;
 1299           try {
 1300               prop = getProperty(theClass, theClass, methodName, false, false);
 1301           } catch (MissingPropertyException mpe) {
 1302               // ignore
 1303           }
 1304   
 1305           if (prop instanceof Closure) {
 1306               return invokeStaticClosureProperty(arguments, prop);
 1307           }
 1308   
 1309           Object[] originalArguments = (Object[]) arguments.clone();
 1310           MetaClassHelper.unwrap(arguments);
 1311   
 1312           Class superClass = sender.getSuperclass();
 1313           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
 1314           while (superClass != Object.class && superClass != null) {
 1315               MetaClass mc = registry.getMetaClass(superClass);
 1316               method = mc.getStaticMetaMethod(methodName, argClasses);
 1317               if (method != null) return method.doMethodInvoke(object, arguments);
 1318   
 1319               try {
 1320                   prop = mc.getProperty(superClass, superClass, methodName, false, false);
 1321               } catch (MissingPropertyException mpe) {
 1322                   // ignore
 1323               }
 1324   
 1325               if (prop instanceof Closure) {
 1326                   return invokeStaticClosureProperty(originalArguments, prop);
 1327               }
 1328   
 1329               superClass = superClass.getSuperclass();
 1330           }
 1331           
 1332           if(prop != null) {
 1333           	MetaClass propMC = registry.getMetaClass(prop.getClass());
 1334       		return propMC.invokeMethod(prop, CLOSURE_CALL_METHOD, arguments);
 1335           }
 1336   
 1337           return invokeStaticMissingMethod(sender, methodName, arguments);
 1338       }
 1339   
 1340       private Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) {
 1341           Closure closure = (Closure) prop;
 1342           MetaClass delegateMetaClass = closure.getMetaClass();
 1343           return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, false);
 1344       }
 1345   
 1346       private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) {
 1347           MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, METHOD_MISSING_ARGS);
 1348           if (metaMethod != null) {
 1349               return metaMethod.invoke(sender, new Object[]{methodName, arguments});
 1350           }
 1351           throw new MissingMethodException(methodName, sender, arguments, true);
 1352       }
 1353   
 1354       private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
 1355           MetaMethod method = null;
 1356           MethodSelectionException mse = null;
 1357           Object methods = getStaticMethods(theClass, methodName);
 1358   
 1359           if (!(methods instanceof FastArray) || !((FastArray)methods).isEmpty()) {
 1360               try {
 1361                   method = (MetaMethod) chooseMethod(methodName, methods, arguments);
 1362               } catch(MethodSelectionException msex) {
 1363                   mse = msex;
 1364               }
 1365           }
 1366           if (method == null && theClass != Class.class) {
 1367               MetaClass classMetaClass = registry.getMetaClass(Class.class);
 1368               method = classMetaClass.pickMethod(methodName, arguments);
 1369           }
 1370           if (method == null) {
 1371               method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments));
 1372           }
 1373   
 1374           if(method == null && mse != null) {
 1375           	throw mse;
 1376           } else {
 1377               return method;
 1378           }
 1379       }
 1380   
 1381       /**
 1382        * Warning, this method will be removed
 1383        *
 1384        * @deprecated use invokeConstructor instead
 1385        */
 1386       public Object invokeConstructorAt(Class at, Object[] arguments) {
 1387           return invokeConstructor(arguments);
 1388       }
 1389   
 1390       public Object invokeConstructor(Object[] arguments) {
 1391           return invokeConstructor(theClass, arguments);
 1392       }
 1393   
 1394       public int selectConstructorAndTransformArguments(int numberOfConstructors, Object[] arguments) {
 1395           //TODO: that is just a quick prototype, not the real thing!
 1396           if (numberOfConstructors != constructors.size()) {
 1397               throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for " +
 1398                       this.theClass.getName() + " do not match. Expected " + numberOfConstructors + " but got " + constructors.size());
 1399           }
 1400   
 1401           if (arguments == null) arguments = EMPTY_ARGUMENTS;
 1402           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
 1403           MetaClassHelper.unwrap(arguments);
 1404           CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
 1405           if (constructor == null) {
 1406               constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
 1407           }
 1408           if (constructor == null) {
 1409               throw new GroovyRuntimeException(
 1410                       "Could not find matching constructor for: "
 1411                               + theClass.getName()
 1412                               + "(" + InvokerHelper.toTypeString(arguments) + ")");
 1413           }
 1414           List l = new ArrayList(constructors.toList());
 1415           Comparator comp = new Comparator() {
 1416               public int compare(Object arg0, Object arg1) {
 1417                   CachedConstructor c0 = (CachedConstructor) arg0;
 1418                   CachedConstructor c1 = (CachedConstructor) arg1;
 1419                   String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes());
 1420                   String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes());
 1421                   return descriptor0.compareTo(descriptor1);
 1422               }
 1423           };
 1424           Collections.sort(l, comp);
 1425           int found = -1;
 1426           for (int i = 0; i < l.size(); i++) {
 1427               if (l.get(i) != constructor) continue;
 1428               found = i;
 1429               break;
 1430           }
 1431           // NOTE: must be changed to "1 |" if constructor was vargs
 1432           return 0 | (found << 8);
 1433       }
 1434   
 1435       /**
 1436        * checks if the initialisation of the class id complete.
 1437        * This method should be called as a form of assert, it is no
 1438        * way to test if there is still initialisation work to be done.
 1439        * Such logic must be implemented in a different way.
 1440        *
 1441        * @throws IllegalStateException if the initialisation is incomplete yet
 1442        */
 1443       protected void checkInitalised() {
 1444           if (!isInitialized())
 1445               throw new IllegalStateException(
 1446                       "initialize must be called for meta " +
 1447                               "class of " + theClass +
 1448                               "(" + this.getClass() + ") " +
 1449                               "to complete initialisation process " +
 1450                               "before any invocation or field/property " +
 1451                               "access can be done");
 1452       }
 1453   
 1454       private Object invokeConstructor(Class at, Object[] arguments) {
 1455           checkInitalised();
 1456           if (arguments == null) arguments = EMPTY_ARGUMENTS;
 1457           Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
 1458           MetaClassHelper.unwrap(arguments);
 1459           CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
 1460           if (constructor != null) {
 1461               return constructor.doConstructorInvoke(arguments);
 1462           }
 1463   
 1464           if (arguments.length == 1) {
 1465               Object firstArgument = arguments[0];
 1466               if (firstArgument instanceof Map) {
 1467                   constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY);
 1468                   if (constructor != null) {
 1469                       Object bean = constructor.doConstructorInvoke(MetaClassHelper.EMPTY_ARRAY);
 1470                       setProperties(bean, ((Map) firstArgument));
 1471                       return bean;
 1472                   }
 1473               }
 1474           }
 1475           throw new GroovyRuntimeException(
 1476                   "Could not find matching constructor for: "
 1477                           + theClass.getName()
 1478                           + "(" + InvokerHelper.toTypeString(arguments) + ")");
 1479       }
 1480   
 1481       /**
 1482        * Sets a number of bean properties from the given Map where the keys are
 1483        * the String names of properties and the values are the values of the
 1484        * properties to set
 1485        */
 1486       public void setProperties(Object bean, Map map) {
 1487           checkInitalised();
 1488           for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
 1489               Map.Entry entry = (Map.Entry) iter.next();
 1490               String key = entry.getKey().toString();
 1491   
 1492               Object value = entry.getValue();
 1493               setProperty(bean, key, value);
 1494           }
 1495       }
 1496   
 1497       /**
 1498        * @return the given property's value on the object
 1499        */
 1500       public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
 1501   
 1502           //----------------------------------------------------------------------
 1503           // handling of static
 1504           //----------------------------------------------------------------------
 1505           boolean isStatic = theClass != Class.class && object instanceof Class;
 1506           if (isStatic && object != theClass) {
 1507               MetaClass mc = registry.getMetaClass((Class) object);
 1508               return mc.getProperty(sender, object, name, useSuper, false);
 1509           }
 1510   
 1511           checkInitalised();
 1512   
 1513           //----------------------------------------------------------------------
 1514           // turn getProperty on a Map to get on the Map itself
 1515           //----------------------------------------------------------------------
 1516           if (!isStatic && this.isMap) {
 1517               return ((Map) object).get(name);
 1518           }
 1519   
 1520           MetaMethod method = null;
 1521           Object[] arguments = EMPTY_ARGUMENTS;
 1522   
 1523           //----------------------------------------------------------------------
 1524           // getter
 1525           //----------------------------------------------------------------------
 1526           MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
 1527           if (mp != null) {
 1528               if (mp instanceof MetaBeanProperty) {
 1529                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
 1530                   method = mbp.getGetter();
 1531                   mp = mbp.getField();
 1532               }
 1533           }
 1534   
 1535           // check for a category method named like a getter
 1536           if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 1537               String getterName = "get" + MetaClassHelper.capitalize(name);
 1538               MetaMethod categoryMethod = getCategoryMethodGetter(sender, getterName, false);
 1539               if (categoryMethod != null) method = categoryMethod;
 1540           }
 1541   
 1542           //----------------------------------------------------------------------
 1543           // field
 1544           //----------------------------------------------------------------------
 1545           if (method == null && mp != null) {
 1546               try {
 1547                   return mp.getProperty(object);
 1548               } catch (IllegalArgumentException e) {
 1549                   // can't access the field directly but there may be a getter
 1550                   mp = null;
 1551               }
 1552           }
 1553   
 1554           //----------------------------------------------------------------------
 1555           // generic get method
 1556           //----------------------------------------------------------------------
 1557           // check for a generic get method provided through a category
 1558           if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 1559               method = getCategoryMethodGetter(sender, "get", true);
 1560               if (method != null) arguments = new Object[]{name};
 1561           }
 1562   
 1563           // the generic method is valid, if available (!=null), if static or
 1564           // if it is not static and we do no static access
 1565           if (method == null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
 1566               arguments = new Object[]{name};
 1567               method = genericGetMethod;
 1568           }
 1569   
 1570           //----------------------------------------------------------------------
 1571           // special cases
 1572           //----------------------------------------------------------------------
 1573           if (method == null) {
 1574               /** todo these special cases should be special MetaClasses maybe */
 1575               if (theClass != Class.class && object instanceof Class) {
 1576                   MetaClass mc = registry.getMetaClass(Class.class);
 1577                   return mc.getProperty(Class.class, object, name, useSuper, false);
 1578               } else if (object instanceof Collection) {
 1579                   return DefaultGroovyMethods.getAt((Collection) object, name);
 1580               } else if (object instanceof Object[]) {
 1581                   return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
 1582               } else {
 1583                   MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
 1584                   if (addListenerMethod != null) {
 1585                       //TODO: one day we could try return the previously registered Closure listener for easy removal
 1586                       return null;
 1587                   }
 1588               }
 1589           } else {
 1590   
 1591               //----------------------------------------------------------------------
 1592               // executing the getter method
 1593               //----------------------------------------------------------------------
 1594               return method.doMethodInvoke(object, arguments);
 1595           }
 1596   
 1597           //----------------------------------------------------------------------
 1598           // error due to missing method/field
 1599           //----------------------------------------------------------------------
 1600           if (isStatic || object instanceof Class)
 1601               return invokeStaticMissingProperty(object, name, null, true);
 1602           else
 1603               return invokeMissingProperty(object, name, null, true);
 1604       }
 1605   
 1606       public MetaProperty getEffectiveGetMetaProperty(final Class sender, final Object object, String name, final boolean useSuper) {
 1607   
 1608           //----------------------------------------------------------------------
 1609           // handling of static
 1610           //----------------------------------------------------------------------
 1611           boolean isStatic = theClass != Class.class && object instanceof Class;
 1612           if (isStatic && object != theClass) {
 1613               return new MetaProperty(name, Object.class) {
 1614                   final MetaClass mc = registry.getMetaClass((Class) object);
 1615   
 1616                   public Object getProperty(Object object) {
 1617                       return mc.getProperty(sender, object, name, useSuper,false);
 1618                   }
 1619   
 1620                   public void setProperty(Object object, Object newValue) {
 1621                       throw new UnsupportedOperationException();
 1622                   }
 1623               };
 1624           }
 1625   
 1626           checkInitalised();
 1627   
 1628           //----------------------------------------------------------------------
 1629           // turn getProperty on a Map to get on the Map itself
 1630           //----------------------------------------------------------------------
 1631           if (!isStatic && this.isMap) {
 1632               return new MetaProperty(name, Object.class) {
 1633                   public Object getProperty(Object object) {
 1634                       return ((Map) object).get(name);
 1635                   }
 1636   
 1637                   public void setProperty(Object object, Object newValue) {
 1638                       throw new UnsupportedOperationException();
 1639                   }
 1640               };
 1641           }
 1642   
 1643           MetaMethod method = null;
 1644   
 1645           //----------------------------------------------------------------------
 1646           // getter
 1647           //----------------------------------------------------------------------
 1648           MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
 1649           if (mp != null) {
 1650               if (mp instanceof MetaBeanProperty) {
 1651                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
 1652                   method = mbp.getGetter();
 1653                   mp = mbp.getField();
 1654               }
 1655           }
 1656   
 1657           // check for a category method named like a getter
 1658           if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 1659               String getterName = "get" + MetaClassHelper.capitalize(name);
 1660               MetaMethod categoryMethod = getCategoryMethodGetter(sender, getterName, false);
 1661               if (categoryMethod != null)
 1662                 method = categoryMethod;
 1663           }
 1664   
 1665           //----------------------------------------------------------------------
 1666           // field
 1667           //----------------------------------------------------------------------
 1668           if (method != null)
 1669               return new GetBeanMethodMetaProperty(name, method);
 1670   
 1671           if (mp != null) {
 1672               return mp;
 1673   //            try {
 1674   //                return mp.getProperty(object);
 1675   //            } catch (IllegalArgumentException e) {
 1676   //                // can't access the field directly but there may be a getter
 1677   //                mp = null;
 1678   //            }
 1679           }
 1680   
 1681           //----------------------------------------------------------------------
 1682           // generic get method
 1683           //----------------------------------------------------------------------
 1684           // check for a generic get method provided through a category
 1685           if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 1686               method = getCategoryMethodGetter(sender, "get", true);
 1687               if (method != null)
 1688                   return new GetMethodMetaProperty(name, method);
 1689           }
 1690   
 1691           // the generic method is valid, if available (!=null), if static or
 1692           // if it is not static and we do no static access
 1693           if (genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
 1694               method = genericGetMethod;
 1695               if (method != null)
 1696                   return new GetMethodMetaProperty(name, method);
 1697           }
 1698   
 1699           //----------------------------------------------------------------------
 1700           // special cases
 1701           //----------------------------------------------------------------------
 1702           /** todo these special cases should be special MetaClasses maybe */
 1703           if (theClass != Class.class && object instanceof Class) {
 1704               return new MetaProperty(name, Object.class) {
 1705                   public Object getProperty(Object object) {
 1706                       MetaClass mc = registry.getMetaClass(Class.class);
 1707                       return mc.getProperty(Class.class, object, name, useSuper, false);
 1708                   }
 1709   
 1710                   public void setProperty(Object object, Object newValue) {
 1711                       throw new UnsupportedOperationException();
 1712                   }
 1713               };
 1714           } else if (object instanceof Collection) {
 1715               return new MetaProperty(name, Object.class) {
 1716                   public Object getProperty(Object object) {
 1717                       return DefaultGroovyMethods.getAt((Collection) object, name);
 1718                   }
 1719   
 1720                   public void setProperty(Object object, Object newValue) {
 1721                       throw new UnsupportedOperationException();
 1722                   }
 1723               };
 1724           } else if (object instanceof Object[]) {
 1725               return new MetaProperty(name, Object.class) {
 1726                   public Object getProperty(Object object) {
 1727                       return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
 1728                   }
 1729   
 1730                   public void setProperty(Object object, Object newValue) {
 1731                       throw new UnsupportedOperationException();
 1732                   }
 1733               };
 1734           } else {
 1735               MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
 1736               if (addListenerMethod != null) {
 1737                   //TODO: one day we could try return the previously registered Closure listener for easy removal
 1738                   return new MetaProperty(name, Object.class) {
 1739                       public Object getProperty(Object object) {
 1740                           return null;
 1741                       }
 1742   
 1743                       public void setProperty(Object object, Object newValue) {
 1744                           throw new UnsupportedOperationException();
 1745                       }
 1746                   };
 1747               }
 1748           }
 1749   
 1750           //----------------------------------------------------------------------
 1751           // error due to missing method/field
 1752           //----------------------------------------------------------------------
 1753           if (isStatic || object instanceof Class)
 1754               return new MetaProperty(name, Object.class) {
 1755                   public Object getProperty(Object object) {
 1756                       return invokeStaticMissingProperty(object, name, null, true);
 1757                   }
 1758   
 1759                   public void setProperty(Object object, Object newValue) {
 1760                       throw new UnsupportedOperationException();
 1761                   }
 1762               };
 1763           else
 1764               return new MetaProperty(name, Object.class) {
 1765                   public Object getProperty(Object object) {
 1766                       return invokeMissingProperty(object, name, null, true);
 1767                   }
 1768   
 1769                   public void setProperty(Object object, Object newValue) {
 1770                       throw new UnsupportedOperationException();
 1771                   }
 1772               };
 1773       }
 1774   
 1775   
 1776   
 1777       private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
 1778           List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(name);
 1779           if (possibleGenericMethods != null) {
 1780               for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
 1781                   MetaMethod mmethod = (MetaMethod) iter.next();
 1782                   if (!mmethod.getDeclaringClass().getTheClass().isAssignableFrom(sender))
 1783                     continue;
 1784   
 1785                   CachedClass[] paramTypes = mmethod.getParameterTypes();
 1786                   if (useLongVersion) {
 1787                       if (paramTypes.length == 1 && paramTypes[0].getTheClass() == String.class) {
 1788                           return mmethod;
 1789                       }
 1790                   } else {
 1791                       if (paramTypes.length == 0) return mmethod;
 1792                   }
 1793               }
 1794           }
 1795           return null;
 1796       }
 1797   
 1798       private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
 1799           List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(name);
 1800           if (possibleGenericMethods != null) {
 1801               for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
 1802                   MetaMethod mmethod = (MetaMethod) iter.next();
 1803                   if (!mmethod.getDeclaringClass().getTheClass().isAssignableFrom(sender))
 1804                     continue;
 1805   
 1806                   CachedClass[] paramTypes = mmethod.getParameterTypes();
 1807                   if (useLongVersion) {
 1808                       if (paramTypes.length == 2 && paramTypes[0].getTheClass() == String.class) {
 1809                           return mmethod;
 1810                       }
 1811                   } else {
 1812                       if (paramTypes.length == 1) return mmethod;
 1813                   }
 1814               }
 1815           }
 1816           return null;
 1817       }
 1818   
 1819       /**
 1820        * Get all the properties defined for this type
 1821        *
 1822        * @return a list of MetaProperty objects
 1823        */
 1824       public List<MetaProperty> getProperties() {
 1825           checkInitalised();
 1826           SingleKeyHashMap propertyMap = classPropertyIndex.getNullable(theCachedClass);
 1827           // simply return the values of the metaproperty map as a List
 1828           List ret = new ArrayList(propertyMap.size());
 1829           for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
 1830               MetaProperty element = (MetaProperty) ((SingleKeyHashMap.Entry) iter.next()).value;
 1831               if (element instanceof CachedField) continue;
 1832               // filter out DGM beans
 1833               if (element instanceof MetaBeanProperty) {
 1834                   MetaBeanProperty mp = (MetaBeanProperty) element;
 1835                   boolean setter = true;
 1836                   boolean getter = true;
 1837                   if (mp.getGetter() == null || mp.getGetter() instanceof GeneratedMetaMethod || mp.getGetter() instanceof NewInstanceMetaMethod) {
 1838                       getter = false;
 1839                   }
 1840                   if (mp.getSetter() == null || mp.getSetter() instanceof GeneratedMetaMethod || mp.getSetter() instanceof NewInstanceMetaMethod) {
 1841                       setter = false;
 1842                   }
 1843                   if (!setter && !getter) continue;
 1844   //  TODO: I (ait) don't know why these strange tricks needed and comment following as it effects some Grails tests             
 1845   //                if (!setter && mp.getSetter() != null) {
 1846   //                    element = new MetaBeanProperty(mp.getName(), mp.getType(), mp.getGetter(), null);
 1847   //                }
 1848   //                if (!getter && mp.getGetter() != null) {
 1849   //                    element = new MetaBeanProperty(mp.getName(), mp.getType(), null, mp.getSetter());
 1850   //                }
 1851               }
 1852               ret.add(element);
 1853           }
 1854           return ret;
 1855       }
 1856   
 1857       private MetaMethod findPropertyMethod(Object methodOrList, boolean isGetter) {
 1858           if (methodOrList == null)
 1859             return null;
 1860   
 1861           Object ret = null;
 1862           if (methodOrList instanceof MetaMethod) {
 1863               MetaMethod element = (MetaMethod)methodOrList;
 1864               if (!isGetter &&
 1865                       //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
 1866                       element.getParameterTypes().length == 1) {
 1867                   ret = addElementToList(ret, element);
 1868               }
 1869               if (isGetter &&
 1870                       !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
 1871                       element.getParameterTypes().length == 0) {
 1872                   ret = addElementToList(ret, element);
 1873               }
 1874   
 1875           }
 1876           else {
 1877               FastArray methods = (FastArray) methodOrList;
 1878               final int len = methods.size();
 1879               final Object[] data = methods.getArray();
 1880               for (int i = 0; i != len; ++i) {
 1881                   MetaMethod element = (MetaMethod) data[i];
 1882                   if (!isGetter &&
 1883                           //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
 1884                           element.getParameterTypes().length == 1) {
 1885                       ret = addElementToList(ret, element);
 1886                   }
 1887                   if (isGetter &&
 1888                           !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
 1889                           element.getParameterTypes().length == 0) {
 1890                       ret = addElementToList(ret, element);
 1891                   }
 1892               }
 1893           }
 1894   
 1895           if (ret == null) return null;
 1896           if (ret instanceof MetaMethod) return (MetaMethod) ret;
 1897   
 1898           // we found multiple matching methods
 1899           // this is a problem, because we can use only one
 1900           // if it is a getter, then use the most general return
 1901           // type to decide which method to use. If it is a setter
 1902           // we use the type of the first parameter
 1903           MetaMethod method = null;
 1904           int distance = -1;
 1905           for (Iterator iter = ((List) ret).iterator(); iter.hasNext();) {
 1906               MetaMethod element = (MetaMethod) iter.next();
 1907               Class c;
 1908               if (isGetter) {
 1909                   c = element.getReturnType();
 1910               } else {
 1911                   c = element.getParameterTypes()[0].getTheClass();
 1912               }
 1913               int localDistance = distanceToObject(c);
 1914               //TODO: maybe implement the case localDistance==distance
 1915               if (distance == -1 || distance > localDistance) {
 1916                   distance = localDistance;
 1917                   method = element;
 1918               }
 1919           }
 1920           return method;
 1921       }
 1922   
 1923       private Object addElementToList(Object ret, MetaMethod element) {
 1924           if (ret == null)
 1925               ret = element;
 1926           else if (ret instanceof List)
 1927               ((List) ret).add(element);
 1928           else {
 1929               List list = new LinkedList();
 1930               list.add(ret);
 1931               list.add(element);
 1932               ret = list;
 1933           }
 1934           return ret;
 1935       }
 1936   
 1937       private static int distanceToObject(Class c) {
 1938           int count;
 1939           for (count = 0; c != null; count++) {
 1940               c = c.getSuperclass();
 1941           }
 1942           return count;
 1943       }
 1944   
 1945   
 1946       /**
 1947        * This will build up the property map (Map of MetaProperty objects, keyed on
 1948        * property name).
 1949        */
 1950       private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
 1951           if (theCachedClass.isInterface) {
 1952               LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>();
 1953               superClasses.add(ReflectionCache.OBJECT_CLASS);
 1954               Set interfaces = theCachedClass.getInterfaces();
 1955   
 1956               classPropertyIndexForSuper = classPropertyIndex;
 1957               final SingleKeyHashMap cPI = classPropertyIndex.getNotNull(theCachedClass);
 1958               for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
 1959                   CachedClass iclass = (CachedClass) interfaceIter.next();
 1960                   SingleKeyHashMap iPropertyIndex = cPI;
 1961                   addFields(iclass, iPropertyIndex);
 1962                   classPropertyIndex.put(iclass, iPropertyIndex);
 1963               }
 1964               classPropertyIndex.put(ReflectionCache.OBJECT_CLASS, cPI);
 1965   
 1966               applyPropertyDescriptors(propertyDescriptors);
 1967               applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
 1968   
 1969               makeStaticPropertyIndex();
 1970           } else {
 1971               LinkedList<CachedClass> superClasses = getSuperClasses();
 1972               Set interfaces = theCachedClass.getInterfaces();
 1973   
 1974               // if this an Array, then add the special read-only "length" property
 1975               if (theCachedClass.isArray) {
 1976                   SingleKeyHashMap map = new SingleKeyHashMap();
 1977                   map.put("length", arrayLengthProperty);
 1978                   classPropertyIndex.put(theCachedClass, map);
 1979               }
 1980   
 1981               inheritStaticInterfaceFields(superClasses, interfaces);
 1982               inheritFields(superClasses);
 1983   
 1984               applyPropertyDescriptors(propertyDescriptors);
 1985   
 1986               applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
 1987               applyStrayPropertyMethods(superClasses, classPropertyIndexForSuper, false);
 1988   
 1989               copyClassPropertyIndexForSuper(classPropertyIndexForSuper);
 1990               makeStaticPropertyIndex();
 1991           }
 1992       }
 1993   
 1994       private void makeStaticPropertyIndex() {
 1995           SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
 1996           for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
 1997               SingleKeyHashMap.Entry entry = ((SingleKeyHashMap.Entry) iter.next());
 1998   
 1999               MetaProperty mp = (MetaProperty) entry.getValue();
 2000               if (mp instanceof CachedField) {
 2001                   CachedField mfp = (CachedField) mp;
 2002                   if (!mfp.isStatic()) continue;
 2003               } else if (mp instanceof MetaBeanProperty) {
 2004                   MetaProperty result = establishStaticMetaProperty(mp);
 2005                   if (result == null) continue;
 2006                   else {
 2007                       mp = result;
 2008                   }
 2009               } else {
 2010                   continue; // ignore all other types
 2011               }
 2012               staticPropertyIndex.put(entry.getKey(), mp);
 2013           }
 2014   
 2015       }
 2016   
 2017       private MetaProperty establishStaticMetaProperty(MetaProperty mp) {
 2018           MetaBeanProperty mbp = (MetaBeanProperty) mp;
 2019           MetaProperty result = null;
 2020           final MetaMethod getterMethod = mbp.getGetter();
 2021           final MetaMethod setterMethod = mbp.getSetter();
 2022           final CachedField metaField = mbp.getField();
 2023   
 2024           boolean getter = getterMethod == null || getterMethod.isStatic();
 2025           boolean setter = setterMethod == null || setterMethod.isStatic();
 2026           boolean field = metaField == null || metaField.isStatic();
 2027   
 2028           if (!getter && !setter && !field) {
 2029               return result;
 2030           } else {
 2031               final String propertyName = mbp.getName();
 2032               final Class propertyType = mbp.getType();
 2033   
 2034               if (setter && getter) {
 2035                   if (field) {
 2036                       result = mbp; // nothing to do
 2037                   } else {
 2038                       result = new MetaBeanProperty(propertyName, propertyType, getterMethod, setterMethod);
 2039                   }
 2040               } else if (getter && !setter) {
 2041                   if (getterMethod == null) {
 2042                       result = metaField;
 2043                   } else {
 2044                       MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, getterMethod, null);
 2045                       if (field) newmp.setField(metaField);
 2046                       result = newmp;
 2047                   }
 2048               } else if (setter && !getter) {
 2049                   if (setterMethod == null) {
 2050                       result = metaField;
 2051                   } else {
 2052                       MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, null, setterMethod);
 2053                       if (field) newmp.setField(metaField);
 2054                       result = newmp;
 2055                   }
 2056               } else
 2057                   result = metaField;
 2058           }
 2059           return result;
 2060       }
 2061   
 2062       private void copyClassPropertyIndexForSuper(Index dest) {
 2063           for (ComplexKeyHashMap.EntryIterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext();) {
 2064               SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
 2065               SingleKeyHashMap newVal = new SingleKeyHashMap();
 2066               dest.put((CachedClass) entry.getKey(), newVal);
 2067           }
 2068       }
 2069   
 2070       private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
 2071           for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
 2072               CachedClass iclass = (CachedClass) interfaceIter.next();
 2073               SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(iclass);
 2074               addFields(iclass, iPropertyIndex);
 2075               for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
 2076                   CachedClass sclass = (CachedClass) classIter.next();
 2077                   if (!iclass.getTheClass().isAssignableFrom(sclass.getTheClass())) continue;
 2078                   SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(sclass);
 2079                   copyNonPrivateFields(iPropertyIndex, sPropertyIndex);
 2080               }
 2081           }
 2082       }
 2083   
 2084       private void inheritFields(LinkedList<CachedClass> superClasses) {
 2085           SingleKeyHashMap last = null;
 2086           for (CachedClass klass : superClasses) {
 2087               SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
 2088               if (last != null) {
 2089                   copyNonPrivateFields(last, propertyIndex);
 2090               }
 2091               last = propertyIndex;
 2092               addFields(klass, propertyIndex);
 2093           }
 2094       }
 2095   
 2096       private void addFields(final CachedClass klass, SingleKeyHashMap propertyIndex) {
 2097           CachedField[] fields = klass.getFields();
 2098           for (CachedField field : fields) {
 2099               propertyIndex.put(field.getName(), field);
 2100           }
 2101       }
 2102   
 2103       private void copyNonPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to) {
 2104           for (ComplexKeyHashMap.EntryIterator iter = from.getEntrySetIterator(); iter.hasNext();) {
 2105               SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
 2106               CachedField mfp = (CachedField) entry.getValue();
 2107               if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
 2108               to.put(entry.getKey(), mfp);
 2109           }
 2110       }
 2111   
 2112       private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Index classPropertyIndex, boolean isThis) {
 2113           // now look for any stray getters that may be used to define a property
 2114           for (CachedClass klass : superClasses) {
 2115               MetaMethodIndex.Header header = metaMethodIndex.getHeader(klass.getTheClass());
 2116               SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
 2117               for (MetaMethodIndex.Entry e = header.head; e != null; e = e.nextClassEntry) {
 2118                   String methodName = e.name;
 2119                   // name too short?
 2120                   if (methodName.length() < 4) continue;
 2121                   // possible getter/setter?
 2122                   boolean isGetter = methodName.startsWith("get");
 2123                   boolean isSetter = methodName.startsWith("set");
 2124                   if (!isGetter && !isSetter) continue;
 2125   
 2126                   MetaMethod propertyMethod = findPropertyMethod(isThis ? e.methods : e.methodsForSuper, isGetter);
 2127                   if (propertyMethod == null) continue;
 2128   
 2129                   String propName = getPropName(methodName);
 2130                   createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
 2131               }
 2132           }
 2133       }
 2134   
 2135       private static final HashMap<String, String> propNames = new HashMap<String, String>(1024);
 2136   
 2137       private String getPropName(String methodName) {
 2138           String name = propNames.get(methodName);
 2139           if (name != null)
 2140               return name;
 2141   
 2142           synchronized (propNames) {
 2143               String propName = java.beans.Introspector.decapitalize(methodName.substring(3));
 2144               propNames.put(methodName, propName);
 2145               return propName;
 2146           }
 2147       }
 2148   
 2149       private void createMetaBeanProperty(SingleKeyHashMap propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
 2150           // is this property already accounted for?
 2151           MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
 2152           if (mp == null) {
 2153               if (isGetter) {
 2154                   mp = new MetaBeanProperty(propName,
 2155                           propertyMethod.getReturnType(),
 2156                           propertyMethod, null);
 2157               } else {
 2158                   //isSetter
 2159                   mp = new MetaBeanProperty(propName,
 2160                           propertyMethod.getParameterTypes()[0].getTheClass(),
 2161                           null, propertyMethod);
 2162               }
 2163           } else {
 2164               MetaBeanProperty mbp;
 2165               CachedField mfp;
 2166               if (mp instanceof MetaBeanProperty) {
 2167                   mbp = (MetaBeanProperty) mp;
 2168                   mfp = mbp.getField();
 2169               } else if (mp instanceof CachedField) {
 2170                   mfp = (CachedField) mp;
 2171                   mbp = new MetaBeanProperty(propName,
 2172                           mfp.getType(),
 2173                           null, null);
 2174               } else {
 2175                   throw new GroovyBugError("unknown MetaProperty class used. Class is " + mp.getClass());
 2176               }
 2177               // we may have already found one for this name
 2178               if (isGetter && mbp.getGetter() == null) {
 2179                   mbp.setGetter(propertyMethod);
 2180               } else if (!isGetter && mbp.getSetter() == null) {
 2181                   mbp.setSetter(propertyMethod);
 2182               }
 2183               mbp.setField(mfp);
 2184               mp = mbp;
 2185           }
 2186           propertyIndex.put(propName, mp);
 2187       }
 2188   
 2189       private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
 2190           // now iterate over the map of property descriptors and generate
 2191           // MetaBeanProperty objects
 2192           for (PropertyDescriptor pd : propertyDescriptors) {
 2193               // skip if the property type is unknown (this seems to be the case if the
 2194               // property descriptor is based on a setX() method that has two parameters,
 2195               // which is not a valid property)
 2196               if (pd.getPropertyType() == null)
 2197                   continue;
 2198   
 2199               // get the getter method
 2200               Method method = pd.getReadMethod();
 2201               MetaMethod getter;
 2202   
 2203               if (method != null) {
 2204                   CachedMethod cachedGetter = CachedMethod.find(method);
 2205                   getter = cachedGetter == null ? null : findMethod(cachedGetter);
 2206               } else {
 2207                   getter = null;
 2208               }
 2209   
 2210               // get the setter method
 2211               MetaMethod setter;
 2212               method = pd.getWriteMethod();
 2213               if (method != null) {
 2214                   CachedMethod cachedSetter = CachedMethod.find(method);
 2215                   setter = cachedSetter == null ? null : findMethod(cachedSetter);
 2216               } else {
 2217                   setter = null;
 2218               }
 2219   
 2220               // now create the MetaProperty object
 2221               MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
 2222               addMetaBeanProperty(mp);
 2223           }
 2224       }
 2225   
 2226       /**
 2227        * Adds a new MetaBeanProperty to this MetaClass
 2228        *
 2229        * @param mp The MetaBeanProperty
 2230        */
 2231       public void addMetaBeanProperty(MetaBeanProperty mp) {
 2232   
 2233           MetaProperty staticProperty = establishStaticMetaProperty(mp);
 2234           if (staticProperty != null) {
 2235               staticPropertyIndex.put(mp.getName(), mp);
 2236           } else {
 2237   
 2238               SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
 2239               //keep field
 2240               CachedField field;
 2241               MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
 2242               if (old != null) {
 2243                   if (old instanceof MetaBeanProperty) {
 2244                       field = ((MetaBeanProperty) old).getField();
 2245                   } else {
 2246                       field = (CachedField) old;
 2247                   }
 2248                   mp.setField(field);
 2249               }
 2250   
 2251               // put it in the list
 2252               // this will overwrite a possible field property
 2253               propertyMap.put(mp.getName(), mp);
 2254           }
 2255   
 2256       }
 2257   
 2258       /**
 2259        * Sets the property value on an object
 2260        */
 2261       public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
 2262           checkInitalised();
 2263   
 2264           //----------------------------------------------------------------------
 2265           // handling of static
 2266           //----------------------------------------------------------------------
 2267           boolean isStatic = theClass != Class.class && object instanceof Class;
 2268           if (isStatic && object != theClass) {
 2269               MetaClass mc = registry.getMetaClass((Class) object);
 2270               mc.getProperty(sender, object, name, useSuper, fromInsideClass);
 2271               return;
 2272           }
 2273   
 2274           //----------------------------------------------------------------------
 2275           // Unwrap wrapped values fo now - the new MOP will handle them properly
 2276           //----------------------------------------------------------------------
 2277           if (newValue instanceof Wrapper) newValue = ((Wrapper) newValue).unwrap();
 2278   
 2279           MetaMethod method = null;
 2280           Object[] arguments = null;
 2281   
 2282           //----------------------------------------------------------------------
 2283           // setter
 2284           //----------------------------------------------------------------------
 2285           MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
 2286           MetaProperty field = null;
 2287           if (mp != null) {
 2288               if (mp instanceof MetaBeanProperty) {
 2289                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
 2290                   method = mbp.getSetter();
 2291                   MetaProperty f = mbp.getField();
 2292                   if (method != null || (f != null && !Modifier.isFinal(f.getModifiers()))) {
 2293                       arguments = new Object[]{newValue};
 2294                       field = f;
 2295                   }
 2296               } else {
 2297                   field = mp;
 2298               }
 2299           }
 2300   
 2301           // check for a category method named like a setter
 2302           if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 2303               String getterName = "set" + MetaClassHelper.capitalize(name);
 2304               MetaMethod categoryMethod = getCategoryMethodSetter(sender, getterName, false);
 2305               if (categoryMethod != null) {
 2306                   method = categoryMethod;
 2307                   arguments = new Object[]{newValue};
 2308               }
 2309           }
 2310   
 2311           //----------------------------------------------------------------------
 2312           // listener method
 2313           //----------------------------------------------------------------------
 2314           boolean ambiguousListener = false;
 2315           if (method == null) {
 2316               method = (MetaMethod) listeners.get(name);
 2317               ambiguousListener = method == AMBIGUOUS_LISTENER_METHOD;
 2318               if (method != null &&
 2319                       !ambiguousListener &&
 2320                       newValue instanceof Closure) {
 2321                   // lets create a dynamic proxy
 2322                   Object proxy = Proxy.newProxyInstance(
 2323                           theClass.getClassLoader(),
 2324                           new Class[]{method.getParameterTypes()[0].getTheClass()},
 2325                           new ConvertedClosure((Closure) newValue, name));
 2326                   arguments = new Object[]{proxy};
 2327                   newValue = proxy;
 2328               } else {
 2329                   method = null;
 2330               }
 2331           }
 2332   
 2333           //----------------------------------------------------------------------
 2334           // field
 2335           //----------------------------------------------------------------------
 2336           if (method == null && field != null) {
 2337               if (Modifier.isFinal(field.getModifiers())) {
 2338                   throw new ReadOnlyPropertyException(name, theClass);
 2339               }
 2340               if(!(this.isMap && isPrivateOrPkgPrivate(field.getModifiers()))) {
 2341                   field.setProperty(object, newValue);
 2342                   return;
 2343               }
 2344           }
 2345   
 2346           //----------------------------------------------------------------------
 2347           // generic set method
 2348           //----------------------------------------------------------------------
 2349           // check for a generic get method provided through a category
 2350           if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
 2351               method = getCategoryMethodSetter(sender, "set", true);
 2352               if (method != null) arguments = new Object[]{name, newValue};
 2353           }
 2354   
 2355           // the generic method is valid, if available (!=null), if static or
 2356           // if it is not static and we do no static access
 2357           if (method == null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
 2358               arguments = new Object[]{name, newValue};
 2359               method = genericSetMethod;
 2360           }
 2361   
 2362           //----------------------------------------------------------------------
 2363           // executing the getter method
 2364           //----------------------------------------------------------------------
 2365           if (method != null) {
 2366               if (arguments.length == 1) {
 2367                   newValue = DefaultTypeTransformation.castToType(
 2368                           newValue,
 2369                           method.getParameterTypes()[0].getTheClass());
 2370                   arguments[0] = newValue;
 2371               } else {
 2372                   newValue = DefaultTypeTransformation.castToType(
 2373                           newValue,
 2374                           method.getParameterTypes()[1].getTheClass());
 2375                   arguments[1] = newValue;
 2376               }
 2377               method.doMethodInvoke(object, arguments);
 2378               return;
 2379           }
 2380   
 2381           //----------------------------------------------------------------------
 2382           // turn setProperty on a Map to put on the Map itself
 2383           //----------------------------------------------------------------------
 2384           if (!isStatic && this.isMap) {
 2385               ((Map) object).put(name, newValue);
 2386               return;
 2387           }
 2388   
 2389   
 2390           //----------------------------------------------------------------------
 2391           // error due to missing method/field
 2392           //----------------------------------------------------------------------
 2393           if (ambiguousListener) {
 2394               throw new GroovyRuntimeException("There are multiple listeners for the property " + name + ". Please do not use the bean short form to access this listener.");
 2395           }
 2396           if (mp != null) {
 2397               throw new ReadOnlyPropertyException(name, theClass);
 2398           }
 2399   
 2400           invokeMissingProperty(object, name, newValue, false);
 2401       }
 2402   
 2403       private boolean isPrivateOrPkgPrivate(int mod) {
 2404       	return !Modifier.isProtected(mod) && !Modifier.isPublic(mod); 
 2405       }
 2406       
 2407       private MetaProperty getMetaProperty(Class _clazz, String name, boolean useSuper, boolean useStatic) {
 2408           if (_clazz == theClass)
 2409             return getMetaProperty(name, useStatic);
 2410   
 2411           CachedClass clazz = ReflectionCache.getCachedClass(_clazz);
 2412           while (true) {
 2413               SingleKeyHashMap propertyMap;
 2414               if (useStatic) {
 2415                   propertyMap = staticPropertyIndex;
 2416               } else if (useSuper) {
 2417                   propertyMap = classPropertyIndexForSuper.getNullable(clazz);
 2418               } else {
 2419                   propertyMap = classPropertyIndex.getNullable(clazz);
 2420               }
 2421               if (propertyMap == null) {
 2422                   if (clazz != theCachedClass) {
 2423                       clazz = theCachedClass;
 2424                       continue;
 2425                   } else {
 2426                       return null;
 2427                   }
 2428               }
 2429               return (MetaProperty) propertyMap.get(name);
 2430           }
 2431       }
 2432   
 2433   
 2434       private MetaProperty getMetaProperty(String name, boolean useStatic) {
 2435           CachedClass clazz = theCachedClass;
 2436           SingleKeyHashMap propertyMap;
 2437           if (useStatic) {
 2438               propertyMap = staticPropertyIndex;
 2439           } else {
 2440               propertyMap = classPropertyIndex.getNullable(clazz);
 2441           }
 2442           if (propertyMap == null) {
 2443               return null;
 2444           }
 2445           return (MetaProperty) propertyMap.get(name);
 2446       }
 2447   
 2448   
 2449       public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
 2450           return getAttribute(receiver, messageName);
 2451       }
 2452   
 2453       /**
 2454        * Looks up the given attribute (field) on the given object
 2455        */
 2456       public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
 2457           checkInitalised();
 2458   
 2459           boolean isStatic = theClass != Class.class && object instanceof Class;
 2460           if (isStatic && object != theClass) {
 2461               MetaClass mc = registry.getMetaClass((Class) object);
 2462               return mc.getAttribute(sender, object, attribute, useSuper);
 2463           }
 2464   
 2465           MetaProperty mp = getMetaProperty(sender, attribute, useSuper, isStatic);
 2466   
 2467           if (mp != null) {
 2468               if (mp instanceof MetaBeanProperty) {
 2469                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
 2470                   mp = mbp.getField();
 2471               }
 2472               try {
 2473                   // delegate the get operation to the metaproperty
 2474                   if (mp != null) return mp.getProperty(object);
 2475               } catch (Exception e) {
 2476                   throw new GroovyRuntimeException("Cannot read field: " + attribute, e);
 2477               }
 2478           }
 2479   
 2480           throw new MissingFieldException(attribute, theClass);
 2481       }
 2482   
 2483       /**
 2484        * Sets the given attribute (field) on the given object
 2485        */
 2486       public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
 2487           checkInitalised();
 2488   
 2489           boolean isStatic = theClass != Class.class && object instanceof Class;
 2490           if (isStatic && object != theClass) {
 2491               MetaClass mc = registry.getMetaClass((Class) object);
 2492               mc.setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);
 2493               return;
 2494           }
 2495   
 2496           MetaProperty mp = getMetaProperty(sender, attribute, useSuper, isStatic);
 2497   
 2498           if (mp != null) {
 2499               if (mp instanceof MetaBeanProperty) {
 2500                   MetaBeanProperty mbp = (MetaBeanProperty) mp;
 2501                   mp = mbp.getField();
 2502               }
 2503               if (mp != null) {
 2504                   mp.setProperty(object, newValue);
 2505                   return;
 2506               }
 2507           }
 2508   
 2509           throw new MissingFieldException(attribute, theClass);
 2510       }
 2511   
 2512       public ClassNode getClassNode() {
 2513           if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
 2514               // let's try load it from the classpath
 2515               String groovyFile = theClass.getName();
 2516               int idx = groovyFile.indexOf('$');
 2517               if (idx > 0) {
 2518                   groovyFile = groovyFile.substring(0, idx);
 2519               }
 2520               groovyFile = groovyFile.replace('.', '/') + ".groovy";
 2521   
 2522               //System.out.println("Attempting to load: " + groovyFile);
 2523               URL url = theClass.getClassLoader().getResource(groovyFile);
 2524               if (url == null) {
 2525                   url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
 2526               }
 2527               if (url != null) {
 2528                   try {
 2529   
 2530                       /**
 2531                        * todo there is no CompileUnit in scope so class name
 2532                        * checking won't work but that mostly affects the bytecode
 2533                        * generation rather than viewing the AST
 2534                        */
 2535                       CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
 2536                           public void call(ClassVisitor writer, ClassNode node) {
 2537                               if (node.getName().equals(theClass.getName())) {
 2538                                   MetaClassImpl.this.classNode = node;
 2539                               }
 2540                           }
 2541                       };
 2542   
 2543                       final ClassLoader parent = theClass.getClassLoader();
 2544                       GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
 2545                           public Object run() {
 2546                               return new GroovyClassLoader(parent);
 2547                           }
 2548                       });
 2549                       CompilationUnit unit = new CompilationUnit();
 2550                       unit.setClassgenCallback(search);
 2551                       unit.addSource(url);
 2552                       unit.compile(Phases.CLASS_GENERATION);
 2553                   }
 2554                   catch (Exception e) {
 2555                       throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
 2556                   }
 2557               }
 2558   
 2559           }
 2560           return classNode;
 2561       }
 2562   
 2563       public String toString() {
 2564           return super.toString() + "[" + theClass + "]";
 2565       }
 2566   
 2567       // Implementation methods
 2568       //-------------------------------------------------------------------------
 2569   
 2570   
 2571       /**
 2572        * adds a MetaMethod to this class. WARNING: this method will not
 2573        * do the neccessary steps for multimethod logic and using this
 2574        * method doesn't mean, that a method added here is replacing another
 2575        * method from a parent class completely. These steps are usually done
 2576        * by initalize, which means if you need these steps, you have to add
 2577        * the method before running initialize the first time.
 2578        *
 2579        * @param method the MetaMethod
 2580        * @see #initialize()
 2581        */
 2582       public void addMetaMethod(MetaMethod method) {
 2583           if (isInitialized()) {
 2584               throw new RuntimeException("Already initialized, cannot add new method: " + method);
 2585           }
 2586   
 2587           final CachedClass declaringClass = method.getDeclaringClass();
 2588           addMetaMethodToIndex(method, metaMethodIndex.getHeader(declaringClass.getTheClass()));
 2589       }
 2590   
 2591       protected void addMetaMethodToIndex(MetaMethod method, MetaMethodIndex.Header header) {
 2592           checkIfStdMethod(method);
 2593   
 2594           String name = method.getName();
 2595           MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(name, header);
 2596           if (method.isStatic()) {
 2597               e.staticMethods = metaMethodIndex.addMethodToList(e.staticMethods, method);
 2598           }
 2599           e.methods = metaMethodIndex.addMethodToList(e.methods, method);
 2600       }
 2601   
 2602       /**
 2603        * Checks if the metaMethod is a method from the GroovyObject interface such as setProperty, getProperty and invokeMethod
 2604        *
 2605        * @param metaMethod The metaMethod instance
 2606        * @see GroovyObject
 2607        */
 2608       protected final void checkIfGroovyObjectMethod(MetaMethod metaMethod) {
 2609           if (metaMethod instanceof ClosureMetaMethod || metaMethod instanceof MixinInstanceMetaMethod) {
 2610               if(isGetPropertyMethod(metaMethod)) {
 2611                   getPropertyMethod = metaMethod;
 2612               }
 2613               else if(isInvokeMethod(metaMethod)) {
 2614                   invokeMethodMethod = metaMethod;
 2615               }
 2616               else if(isSetPropertyMethod(metaMethod)) {
 2617                   setPropertyMethod = metaMethod;
 2618               }
 2619           }
 2620       }
 2621   
 2622       private boolean isSetPropertyMethod(MetaMethod metaMethod) {
 2623           return SET_PROPERTY_METHOD.equals(metaMethod.getName())  && metaMethod.getParameterTypes().length == 2;
 2624       }
 2625   
 2626       private boolean isGetPropertyMethod(MetaMethod metaMethod) {
 2627           return GET_PROPERTY_METHOD.equals(metaMethod.getName());
 2628       }
 2629   
 2630       private boolean isInvokeMethod(MetaMethod metaMethod) {
 2631           return INVOKE_METHOD_METHOD.equals(metaMethod.getName()) && metaMethod.getParameterTypes().length == 2;
 2632       }
 2633   
 2634       private void checkIfStdMethod(MetaMethod method) {
 2635           checkIfGroovyObjectMethod(method);
 2636   
 2637           if (isGenericGetMethod(method) && genericGetMethod == null) {
 2638               genericGetMethod = method;
 2639           } else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
 2640               genericSetMethod = method;
 2641           }
 2642           if (propertyMissingGet == null && method.getName().equals(PROPERTY_MISSING)) {
 2643               CachedClass[] parameterTypes = method.getParameterTypes();
 2644               if (parameterTypes.length == 1) {
 2645                   propertyMissingGet = method;
 2646               }
 2647           }
 2648           if (propertyMissingSet == null && method.getName().equals(PROPERTY_MISSING)) {
 2649               CachedClass[] parameterTypes = method.getParameterTypes();
 2650               if (parameterTypes.length == 2) {
 2651                   propertyMissingSet = method;
 2652               }
 2653           }
 2654           if (method.getName().equals(METHOD_MISSING)) {
 2655               CachedClass[] parameterTypes = method.getParameterTypes();
 2656               if (parameterTypes.length == 2
 2657                       && parameterTypes[0].getTheClass() == String.class
 2658                       && parameterTypes[1].getTheClass() == Object.class) {
 2659                   methodMissing = method;
 2660               }
 2661           }
 2662   
 2663           if (theCachedClass.isNumber) {
 2664               NumberMathModificationInfo.instance.checkIfStdMethod (method);
 2665           }
 2666       }
 2667   
 2668       protected boolean isInitialized() {
 2669           return initialized;
 2670       }
 2671   
 2672       /**
 2673        * return false: add method
 2674        *        null:  ignore method
 2675        *        true:  replace
 2676        */
 2677       private Boolean getMatchKindForCategory(MetaMethod aMethod, MetaMethod categoryMethod) {
 2678           CachedClass[] params1 = aMethod.getParameterTypes();
 2679           CachedClass[] params2 = categoryMethod.getParameterTypes();
 2680           if (params1.length != params2.length) return Boolean.FALSE;
 2681   
 2682           for (int i = 0; i < params1.length; i++) {
 2683               if (params1[i] != params2[i]) return Boolean.FALSE;
 2684           }
 2685   
 2686           Class aMethodClass = aMethod.getDeclaringClass().getTheClass();
 2687           Class categoryMethodClass = categoryMethod.getDeclaringClass().getTheClass();
 2688   
 2689           if (aMethodClass==categoryMethodClass) return Boolean.TRUE;
 2690           boolean match = aMethodClass.isAssignableFrom(categoryMethodClass);
 2691           if (match) return Boolean.TRUE;
 2692           return null;
 2693       }
 2694   
 2695       private void filterMatchingMethodForCategory(FastArray list, MetaMethod method) {
 2696           int len = list.size();
 2697           if (len==0) {
 2698               list.add(method);
 2699               return;
 2700           }
 2701   
 2702           Object data[] = list.getArray();
 2703           for (int j = 0; j != len; ++j) {
 2704               MetaMethod aMethod = (MetaMethod) data[j];
 2705               Boolean match = getMatchKindForCategory(aMethod, method);
 2706               // true == replace
 2707               if (match==Boolean.TRUE) {
 2708                   list.set(j, method);
 2709                   return;
 2710               // null == ignore (we have a better method already)
 2711               } else if (match==null) {
 2712                   return;
 2713               }
 2714           }
 2715           // the casese true and null for a match are through, the
 2716           // remaining case is false and that means adding the method
 2717           // to our list
 2718           list.add(method);
 2719       }
 2720   
 2721       private int findMatchingMethod(CachedMethod[] data, int from, int to, MetaMethod method) {
 2722           for (int j = from; j <= to; ++j) {
 2723               CachedMethod aMethod = data[j];
 2724               CachedClass[] params1 = aMethod.getParameterTypes();
 2725               CachedClass[] params2 = method.getParameterTypes();
 2726               if (params1.length == params2.length) {
 2727                   boolean matches = true;
 2728                   for (int i = 0; i < params1.length; i++) {
 2729                       if (params1[i] != params2[i]) {
 2730                           matches = false;
 2731                           break;
 2732                       }
 2733                   }
 2734                   if (matches) {
 2735                       return j;
 2736                   }
 2737               }
 2738           }
 2739           return -1;
 2740       }
 2741   
 2742       /**
 2743        * @return the matching method which should be found
 2744        */
 2745       private MetaMethod findMethod(CachedMethod aMethod) {
 2746           Object methods = getMethods(theClass, aMethod.getName(), false);
 2747           if (methods instanceof FastArray) {
 2748               FastArray m  = (FastArray) methods;
 2749               final int len = m.size;
 2750               final Object data[] = m.getArray();
 2751               for (int i = 0; i != len; ++i) {
 2752                   MetaMethod method = (MetaMethod) data[i];
 2753                   if (method.isMethod(aMethod)) {
 2754                       return method;
 2755                   }
 2756               }
 2757           }
 2758           else {
 2759               MetaMethod method = (MetaMethod) methods;
 2760               if (method.getName().equals(aMethod.getName())
 2761   //                    TODO: shoulld be better check for case when only diff in modifiers can be SYNTETIC flag
 2762   //                    && method.getModifiers() == aMethod.getModifiers()
 2763                       && method.getReturnType().equals(aMethod.getReturnType())
 2764                       && MetaMethod.equal(method.getParameterTypes(), aMethod.getParameterTypes())) {
 2765                   return method;
 2766               }
 2767           }
 2768           //log.warning("Creating reflection based dispatcher for: " + aMethod);
 2769           synchronized (aMethod.cachedClass) {
 2770               return aMethod;
 2771           }
 2772       }
 2773   
 2774   
 2775       /**
 2776        * Chooses the correct method to use from a list of methods which match by
 2777        * name.
 2778        *
 2779        * @param methodOrList   the possible methods to choose from
 2780        * @param arguments
 2781        */
 2782       protected Object chooseMethod(String methodName, Object methodOrList, Class[] arguments) {
 2783           if (methodOrList instanceof MetaMethod) {
 2784               if (((ParameterTypes) methodOrList).isValidMethod(arguments)) {
 2785                   return methodOrList;
 2786               }
 2787               return null;
 2788           }
 2789   
 2790           FastArray methods = (FastArray) methodOrList;
 2791           if (methods==null) return null;
 2792           int methodCount = methods.size();
 2793           if (methodCount <= 0) {
 2794               return null;
 2795           } else if (methodCount == 1) {
 2796               Object method = methods.get(0);
 2797               if (((ParameterTypes) method).isValidMethod(arguments)) {
 2798                   return method;
 2799               }
 2800               return null;
 2801           }
 2802           Object answer;
 2803           if (arguments == null || arguments.length == 0) {
 2804               answer = MetaClassHelper.chooseEmptyMethodParams(methods);
 2805           } else if (arguments.length == 1 && arguments[0] == null) {
 2806               answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
 2807           } else {
 2808               Object matchingMethods = null;
 2809   
 2810               final int len = methods.size;
 2811               Object data[] = methods.getArray();
 2812               for (int i = 0; i != len; ++i) {
 2813                   Object method = data[i];
 2814   
 2815                   // making this false helps find matches
 2816                   if (((ParameterTypes) method).isValidMethod(arguments)) {
 2817                       if (matchingMethods == null)
 2818                         matchingMethods = method;
 2819                       else
 2820                           if (matchingMethods instanceof ArrayList)
 2821                             ((ArrayList)matchingMethods).add(method);
 2822                           else {
 2823                               ArrayList arr = new ArrayList(4);
 2824                               arr.add(matchingMethods);
 2825                               arr.add(method);
 2826                               matchingMethods = arr;
 2827                           }
 2828                   }
 2829               }
 2830               if (matchingMethods == null) {
 2831                   return null;
 2832               } else if (!(matchingMethods instanceof ArrayList)) {
 2833                   return matchingMethods;
 2834               }
 2835               return chooseMostSpecificParams(methodName, (List) matchingMethods, arguments);
 2836   
 2837           }
 2838           if (answer != null) {
 2839               return answer;
 2840           }
 2841           throw new MethodSelectionException(methodName, methods, arguments);
 2842       }
 2843   
 2844       private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
 2845   
 2846           long matchesDistance = -1;
 2847           LinkedList matches = new LinkedList();
 2848           for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
 2849               Object method = iter.next();
 2850               ParameterTypes paramTypes = (ParameterTypes) method;
 2851               long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
 2852               if (dist == 0) return method;
 2853               if (matches.size() == 0) {
 2854                   matches.add(method);
 2855                   matchesDistance = dist;
 2856               } else if (dist < matchesDistance) {
 2857                   matchesDistance = dist;
 2858                   matches.clear();
 2859                   matches.add(method);
 2860               } else if (dist == matchesDistance) {
 2861                   matches.add(method);
 2862               }
 2863   
 2864           }
 2865           if (matches.size() == 1) {
 2866               return matches.getFirst();
 2867           }
 2868           if (matches.size() == 0) {
 2869               return null;
 2870           }
 2871   
 2872           //more than one matching method found --> ambiguous!
 2873           String msg = "Ambiguous method overloading for method ";
 2874           msg += theClass.getName() + "#" + name;
 2875           msg += ".\nCannot resolve which method to invoke for ";
 2876           msg += InvokerHelper.toString(arguments);
 2877           msg += " due to overlapping prototypes between:";
 2878           for (Iterator iter = matches.iterator(); iter.hasNext();) {
 2879               Class[] types = ((ParameterTypes) iter.next()).getNativeParameterTypes();
 2880               msg += "\n\t" + InvokerHelper.toString(types);
 2881           }
 2882           throw new GroovyRuntimeException(msg);
 2883       }
 2884   
 2885       private boolean isGenericGetMethod(MetaMethod method) {
 2886           if (method.getName().equals("get")) {
 2887               CachedClass[] parameterTypes = method.getParameterTypes();
 2888               return parameterTypes.length == 1 && parameterTypes[0].getTheClass() == String.class;
 2889           }
 2890           return false;
 2891       }
 2892   
 2893   
 2894       public synchronized void initialize() {
 2895           if (!isInitialized()) {
 2896               fillMethodIndex();
 2897               addProperties();
 2898               initialized = true;
 2899           }
 2900       }
 2901   
 2902       private void addProperties() {
 2903           BeanInfo info;
 2904           final Class stopClass;
 2905           //     introspect
 2906           try {
 2907               if (isBeanDerivative(theClass)) {
 2908                   info = (BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 2909                       public Object run() throws IntrospectionException {
 2910                           return Introspector.getBeanInfo(theClass, Introspector.IGNORE_ALL_BEANINFO);
 2911                       }
 2912                   });
 2913               } else {
 2914                   info = (BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 2915                       public Object run() throws IntrospectionException {
 2916                           return Introspector.getBeanInfo(theClass);
 2917                       }
 2918                   });
 2919               }
 2920           } catch (PrivilegedActionException pae) {
 2921               throw new GroovyRuntimeException("exception during bean introspection", pae.getException());
 2922           }
 2923           PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
 2924           // build up the metaproperties based on the public fields, property descriptors,
 2925           // and the getters and setters
 2926           setupProperties(descriptors);
 2927   
 2928           EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
 2929           for (EventSetDescriptor descriptor : eventDescriptors) {
 2930               Method[] listenerMethods = descriptor.getListenerMethods();
 2931               for (Method listenerMethod : listenerMethods) {
 2932                   final MetaMethod metaMethod = CachedMethod.find(descriptor.getAddListenerMethod());
 2933                   addToAllMethodsIfPublic(metaMethod);
 2934                   String name = listenerMethod.getName();
 2935                   if (listeners.containsKey(name)) {
 2936                       listeners.put(name, AMBIGUOUS_LISTENER_METHOD);
 2937                   } else {
 2938                       listeners.put(name, metaMethod);
 2939                   }
 2940               }
 2941           }
 2942       }
 2943   
 2944       private boolean isBeanDerivative(Class theClass) {
 2945           Class next = theClass;
 2946           while (next != null) {
 2947               if (Arrays.asList(next.getInterfaces()).contains(BeanInfo.class)) return true;
 2948               next = next.getSuperclass();
 2949           }
 2950           return false;
 2951       }
 2952   
 2953       private void addToAllMethodsIfPublic(MetaMethod metaMethod) {
 2954           if (Modifier.isPublic(metaMethod.getModifiers()))
 2955               allMethods.add(metaMethod);
 2956       }
 2957   
 2958       public List<MetaMethod> getMethods() {
 2959           return allMethods;
 2960       }
 2961   
 2962       public List<MetaMethod> getMetaMethods() {
 2963           return new ArrayList<MetaMethod>(newGroovyMethodsSet);
 2964       }
 2965   
 2966       protected void dropStaticMethodCache(String name) {
 2967           metaMethodIndex.clearCaches(name);
 2968       }
 2969   
 2970       protected void dropMethodCache(String name) {
 2971           metaMethodIndex.clearCaches(name);
 2972       }
 2973   
 2974       public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args) {
 2975           if (!(this instanceof AdaptingMetaClass)) {
 2976               Class [] params = MetaClassHelper.convertToTypeArray(args);
 2977               MetaMethod metaMethod = getMethodWithCachingInternal(getTheClass(), site, params);
 2978               if (metaMethod != null)
 2979                  return PojoMetaMethodSite.createPojoMetaMethodSite(site, this, metaMethod, params, receiver, args);
 2980           }
 2981           return new PojoMetaClassSite(site, this);
 2982       }
 2983   
 2984   
 2985       public CallSite createStaticSite(CallSite site, Object[] args) {
 2986           if (!(this instanceof AdaptingMetaClass)) {
 2987               Class [] params = MetaClassHelper.convertToTypeArray(args);
 2988               MetaMethod metaMethod = retrieveStaticMethod(site.getName(), args);
 2989               if (metaMethod != null)
 2990                  return StaticMetaMethodSite.createStaticMetaMethodSite(site, this, metaMethod, params, args);
 2991           }
 2992           return new StaticMetaClassSite(site, this);
 2993       }
 2994   
 2995       public CallSite createPogoCallSite(CallSite site, Object[] args) {
 2996           if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
 2997               Class [] params = MetaClassHelper.convertToTypeArray(args);
 2998               MetaMethod metaMethod = getMethodWithCachingInternal(theClass, site, params);
 2999               if (metaMethod != null)
 3000                  return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args);
 3001           }
 3002           return new PogoMetaClassSite(site, this);
 3003       }
 3004   
 3005       public CallSite createPogoCallCurrentSite(CallSite site, Class sender, Object[] args) {
 3006           if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
 3007             Class [] params = MetaClassHelper.convertToTypeArray(args);
 3008             MetaMethod metaMethod = getMethodWithCachingInternal(sender, site, params);
 3009             if (metaMethod != null)
 3010               return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args);
 3011           }
 3012           return new PogoMetaClassSite(site, this);
 3013       }
 3014   
 3015       public CallSite createConstructorSite(CallSite site, Object[] args) {
 3016           if (!(this instanceof AdaptingMetaClass)) {
 3017               Class[] params = MetaClassHelper.convertToTypeArray(args);
 3018               CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, params);
 3019               if (constructor != null) {
 3020                   return ConstructorSite.createConstructorSite(site, this,constructor,params, args);
 3021               }
 3022               else {
 3023                   if (args.length == 1 && args[0] instanceof Map) {
 3024                       constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY);
 3025                       if (constructor != null) {
 3026                           return new ConstructorSite.NoParamSite(site, this,constructor,params);
 3027                       }
 3028                   }
 3029               }
 3030           }
 3031           return new MetaClassConstructorSite(site, this);
 3032       }
 3033   
 3034       public ClassInfo getClassInfo() {
 3035           return theCachedClass.classInfo;
 3036       }
 3037   
 3038       public int getVersion() {
 3039           return theCachedClass.classInfo.getVersion();
 3040       }
 3041   
 3042       public void incVersion() {
 3043           theCachedClass.classInfo.incVersion();
 3044       }
 3045   
 3046       public MetaMethod[] getAdditionalMetaMethods() {
 3047           return additionalMetaMethods;
 3048       }
 3049   
 3050       protected MetaBeanProperty findPropertyInClassHierarchy(String propertyName, CachedClass theClass) {
 3051           MetaBeanProperty property= null;
 3052           if (theClass == null)
 3053               return null;
 3054   
 3055           final CachedClass superClass = theClass.getCachedSuperClass();
 3056           if (superClass == null)
 3057             return null;
 3058   
 3059           MetaClass metaClass = this.registry.getMetaClass(superClass.getTheClass());
 3060           if(metaClass instanceof MutableMetaClass) {
 3061               property = getMetaPropertyFromMutableMetaClass(propertyName,metaClass);
 3062               if(property == null) {
 3063                   if(superClass != ReflectionCache.OBJECT_CLASS) {
 3064                       property = findPropertyInClassHierarchy(propertyName, superClass);
 3065                   }
 3066                   if(property == null) {
 3067                       final Class[] interfaces = theClass.getTheClass().getInterfaces();
 3068                       property = searchInterfacesForMetaProperty(propertyName, interfaces);
 3069                   }
 3070               }
 3071           }
 3072           return property;
 3073   
 3074       }
 3075   
 3076       private MetaBeanProperty searchInterfacesForMetaProperty(String propertyName, Class[] interfaces) {
 3077           MetaBeanProperty property = null;
 3078           for (Class anInterface : interfaces) {
 3079               MetaClass metaClass = registry.getMetaClass(anInterface);
 3080               if (metaClass instanceof MutableMetaClass) {
 3081                   property = getMetaPropertyFromMutableMetaClass(propertyName, metaClass);
 3082                   if (property != null) break;
 3083               }
 3084               Class[] superInterfaces = anInterface.getInterfaces();
 3085               if (superInterfaces.length > 0) {
 3086                   property = searchInterfacesForMetaProperty(propertyName, superInterfaces);
 3087                   if (property != null) break;
 3088               }
 3089   
 3090           }
 3091           return property;
 3092       }
 3093   
 3094       private MetaBeanProperty getMetaPropertyFromMutableMetaClass(String propertyName, MetaClass metaClass) {
 3095           final boolean isModified = ((MutableMetaClass) metaClass).isModified();
 3096           if (isModified) {
 3097               final MetaProperty metaProperty = metaClass.getMetaProperty(propertyName);
 3098               if(metaProperty instanceof MetaBeanProperty)
 3099                   return (MetaBeanProperty)metaProperty;
 3100           }
 3101           return null;
 3102       }
 3103   
 3104       protected MetaMethod findMixinMethod(String methodName, Class[] arguments) {
 3105           return null;
 3106       }
 3107   
 3108       protected static MetaMethod findMethodInClassHierarchy(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass) {
 3109   
 3110           if (metaClass instanceof MetaClassImpl) {
 3111               boolean check = false;
 3112               for (ClassInfo ci : ((MetaClassImpl)metaClass).theCachedClass.getHierarchy ()) {
 3113                   final MetaClass aClass = ci.getStrongMetaClass();
 3114                   if (aClass instanceof MutableMetaClass && ((MutableMetaClass)aClass).isModified()) {
 3115                       check = true;
 3116                       break;
 3117                   }
 3118               }
 3119   
 3120               if (!check)
 3121                 return null;
 3122           }
 3123   
 3124           MetaMethod method = null;
 3125   
 3126           Class superClass;
 3127           if (metaClass.getTheClass().isArray() && !metaClass.getTheClass().getComponentType().isPrimitive() && metaClass.getTheClass().getComponentType() != Object.class) {
 3128               superClass = Object[].class;
 3129           }
 3130           else {
 3131               superClass = metaClass.getTheClass().getSuperclass();
 3132           }
 3133   
 3134           if (superClass != null) {
 3135             MetaClass superMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(superClass);
 3136             method = findMethodInClassHierarchy(instanceKlazz, methodName, arguments, superMetaClass);
 3137           }
 3138           else {
 3139               if (metaClass.getTheClass().isInterface()) {
 3140                   MetaClass superMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(Object.class);
 3141                   method = findMethodInClassHierarchy(instanceKlazz, methodName, arguments, superMetaClass);
 3142               }
 3143           }
 3144   
 3145           method = findSubClassMethod(instanceKlazz, methodName, arguments, metaClass, method);
 3146   
 3147           MetaMethod infMethod = searchInterfacesForMetaMethod(instanceKlazz, methodName, arguments, metaClass);
 3148           if (infMethod != null) {
 3149               if (method == null)
 3150                 method = infMethod;
 3151               else
 3152                 method = mostSpecific(method, infMethod, instanceKlazz);
 3153           }
 3154   
 3155           method = findOwnMethod(instanceKlazz, methodName, arguments, metaClass, method);
 3156   
 3157           return method;
 3158       }
 3159   
 3160       private static MetaMethod findSubClassMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass, MetaMethod method) {
 3161           if (metaClass instanceof MetaClassImpl) {
 3162               Object list = ((MetaClassImpl) metaClass).getSubclassMetaMethods(methodName);
 3163               if (list != null) {
 3164                   if (list instanceof MetaMethod) {
 3165                       MetaMethod m = (MetaMethod) list;
 3166                       if (m.getDeclaringClass().getTheClass().isAssignableFrom(instanceKlazz)) {
 3167                           if (m.isValidExactMethod(arguments)) {
 3168                               if (method == null)
 3169                                 method = m;
 3170                               else {
 3171                                 method = mostSpecific (method, m, instanceKlazz);
 3172                               }
 3173                           }
 3174                       }
 3175                   }
 3176                   else {
 3177                       FastArray arr = (FastArray) list;
 3178                       for (int i = 0; i != arr.size(); ++i) {
 3179                           MetaMethod m = (MetaMethod) arr.get(i);
 3180                           if (m.getDeclaringClass().getTheClass().isAssignableFrom(instanceKlazz)) {
 3181                               if (m.isValidExactMethod(arguments)) {
 3182                                   if (method == null)
 3183                                     method = m;
 3184                                   else {
 3185                                     method = mostSpecific (method, m, instanceKlazz);
 3186                                   }
 3187                               }
 3188                           }
 3189                       }
 3190                   }
 3191               }
 3192           }
 3193           return method;
 3194       }
 3195   
 3196       private static MetaMethod mostSpecific(MetaMethod method, MetaMethod newMethod, Class instanceKlazz) {
 3197           Class newMethodC = newMethod.getDeclaringClass().getTheClass();
 3198           Class methodC = method.getDeclaringClass().getTheClass();
 3199   
 3200           if (!newMethodC.isAssignableFrom(instanceKlazz))
 3201             return method;
 3202   
 3203           if (newMethodC == methodC)
 3204             return newMethod;
 3205   
 3206           if (newMethodC.isAssignableFrom(methodC)) {
 3207               return method;
 3208           }
 3209   
 3210           if (methodC.isAssignableFrom(newMethodC)) {
 3211               return newMethod;
 3212           }
 3213   
 3214           return newMethod;
 3215       }
 3216   
 3217       private static MetaMethod searchInterfacesForMetaMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass) {
 3218           Class[] interfaces = metaClass.getTheClass().getInterfaces();
 3219   
 3220           MetaMethod method = null;
 3221           for (Class anInterface : interfaces) {
 3222               MetaClass infMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(anInterface);
 3223               MetaMethod infMethod = searchInterfacesForMetaMethod(instanceKlazz, methodName, arguments, infMetaClass);
 3224               if (infMethod != null) {
 3225                   if (method == null)
 3226                       method = infMethod;
 3227                   else
 3228                       method = mostSpecific(method, infMethod, instanceKlazz);
 3229               }
 3230           }
 3231   
 3232           method = findSubClassMethod(instanceKlazz, methodName, arguments, metaClass, method);
 3233   
 3234           method = findOwnMethod(instanceKlazz, methodName, arguments, metaClass, method);
 3235   
 3236           return method;
 3237       }
 3238   
 3239       protected static MetaMethod findOwnMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass, MetaMethod method) {
 3240           // we trick ourselves here
 3241           if (instanceKlazz == metaClass.getTheClass())
 3242             return method;
 3243   
 3244           MetaMethod ownMethod = metaClass.pickMethod(methodName, arguments);
 3245           if (ownMethod != null) {
 3246               if (method == null)
 3247                 method = ownMethod;
 3248               else
 3249                 method = mostSpecific(method, ownMethod, instanceKlazz);
 3250           }
 3251           return method;
 3252       }
 3253   
 3254       protected Object getSubclassMetaMethods(String methodName) {
 3255           return null;
 3256       }
 3257   
 3258       private abstract class MethodIndexAction {
 3259           public void iterate() {
 3260               final ComplexKeyHashMap.Entry[] table = metaMethodIndex.methodHeaders.getTable();
 3261               int len = table.length;
 3262               for (int i = 0; i != len; ++i) {
 3263                   for (SingleKeyHashMap.Entry classEntry = (SingleKeyHashMap.Entry) table[i];
 3264                        classEntry != null;
 3265                        classEntry = (SingleKeyHashMap.Entry) classEntry.next) {
 3266   
 3267                       Class clazz = (Class) classEntry.getKey();
 3268   
 3269                       if (skipClass(clazz)) continue;
 3270   
 3271                       MetaMethodIndex.Header header = (MetaMethodIndex.Header) classEntry.getValue();
 3272                       for (MetaMethodIndex.Entry nameEntry = header.head; nameEntry != null; nameEntry = nameEntry.nextClassEntry) {
 3273                           methodNameAction(clazz, nameEntry);
 3274                       }
 3275                   }
 3276               }
 3277           }
 3278   
 3279           public abstract void methodNameAction(Class clazz, MetaMethodIndex.Entry methods);
 3280   
 3281           public boolean skipClass(Class clazz) {
 3282               return false;
 3283           }
 3284       }
 3285   
 3286       public Object getProperty(Object object, String property) {
 3287           return getProperty(theClass, object, property, false, false);
 3288       }
 3289   
 3290       public void setProperty(Object object, String property, Object newValue) {
 3291           setProperty(theClass, object, property, newValue, false, false);
 3292       }
 3293   
 3294       public Object getAttribute(Object object, String attribute) {
 3295           return getAttribute(theClass, object, attribute, false, false);
 3296       }
 3297   
 3298       public void setAttribute(Object object, String attribute, Object newValue) {
 3299           setAttribute(theClass, object, attribute, newValue, false, false);
 3300       }
 3301   
 3302       public MetaMethod pickMethod(String methodName, Class[] arguments) {
 3303           return getMethodWithoutCaching(theClass, methodName, arguments, false);
 3304       }
 3305   
 3306       /**
 3307        * @deprecated use pickMethod instead
 3308        */
 3309       protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
 3310           return pickMethod(methodName, arguments);
 3311       }
 3312   
 3313       /**
 3314        * remove all method call cache entries. This should be done if a
 3315        * method is added during runtime, but not by using a category.
 3316        */
 3317       protected void clearInvocationCaches() {
 3318           metaMethodIndex.clearCaches ();
 3319       }
 3320   
 3321       private static final SingleKeyHashMap.Copier NAME_INDEX_COPIER = new SingleKeyHashMap.Copier() {
 3322           public Object copy(Object value) {
 3323               if (value instanceof FastArray)
 3324                 return ((FastArray) value).copy();
 3325               else
 3326                 return value;
 3327           }
 3328       };
 3329   
 3330       private static final SingleKeyHashMap.Copier METHOD_INDEX_COPIER = new SingleKeyHashMap.Copier() {
 3331           public Object copy(Object value) {
 3332               return SingleKeyHashMap.copy(new SingleKeyHashMap(false), (SingleKeyHashMap) value, NAME_INDEX_COPIER);
 3333           }
 3334       };
 3335   
 3336       class MethodIndex extends Index {
 3337           public MethodIndex(boolean b) {
 3338               super(false);
 3339           }
 3340   
 3341           public MethodIndex(int size) {
 3342               super(size);
 3343           }
 3344   
 3345           public MethodIndex() {
 3346               super();
 3347           }
 3348   
 3349           MethodIndex copy() {
 3350               return (MethodIndex) SingleKeyHashMap.copy(new MethodIndex(false), this, METHOD_INDEX_COPIER);
 3351           }
 3352   
 3353           protected Object clone() throws CloneNotSupportedException {
 3354               return super.clone();
 3355           }
 3356       }
 3357   
 3358       public static class Index extends SingleKeyHashMap {
 3359   
 3360           public Index(int size) {
 3361           }
 3362   
 3363           public Index() {
 3364           }
 3365   
 3366           public Index(boolean size) {
 3367               super(false);
 3368           }
 3369   
 3370           public SingleKeyHashMap getNotNull(CachedClass key) {
 3371               Entry res = getOrPut(key);
 3372               if (res.value == null) {
 3373                   res.value = new SingleKeyHashMap();
 3374               }
 3375               return (SingleKeyHashMap) res.value;
 3376           }
 3377   
 3378           public void put(CachedClass key, SingleKeyHashMap value) {
 3379               ((Entry) getOrPut(key)).value = value;
 3380           }
 3381   
 3382           public SingleKeyHashMap getNullable(CachedClass clazz) {
 3383               return (SingleKeyHashMap) get(clazz);
 3384           }
 3385   
 3386           public boolean checkEquals(ComplexKeyHashMap.Entry e, Object key) {
 3387               return ((Entry) e).key.equals(key);
 3388           }
 3389       }
 3390   
 3391       private static class DummyMetaMethod extends MetaMethod {
 3392   
 3393           public int getModifiers() {
 3394               return 0;
 3395           }
 3396   
 3397           public String getName() {
 3398               return null;
 3399           }
 3400   
 3401           public Class getReturnType() {
 3402               return null;
 3403           }
 3404   
 3405           public CachedClass getDeclaringClass() {
 3406               return null;
 3407           }
 3408   
 3409           public ParameterTypes getParamTypes() {
 3410               return null;
 3411           }
 3412   
 3413           public Object invoke(Object object, Object[] arguments) {
 3414               return null;
 3415           }
 3416       }
 3417   
 3418       private static class GetMethodMetaProperty extends MetaProperty {
 3419           private final MetaMethod theMethod;
 3420   
 3421           public GetMethodMetaProperty(String name, MetaMethod theMethod) {
 3422               super(name, Object.class);
 3423               this.theMethod = theMethod;
 3424           }
 3425   
 3426           public Object getProperty(Object object) {
 3427               return theMethod.doMethodInvoke(object, new Object[]{name});
 3428           }
 3429   
 3430           public void setProperty(Object object, Object newValue) {
 3431               throw new UnsupportedOperationException();
 3432           }
 3433       }
 3434   
 3435       private static class GetBeanMethodMetaProperty extends MetaProperty {
 3436           private final MetaMethod theMethod;
 3437   
 3438           public GetBeanMethodMetaProperty(String name, MetaMethod theMethod) {
 3439               super(name, Object.class);
 3440               this.theMethod = theMethod;
 3441           }
 3442   
 3443           public Object getProperty(Object object) {
 3444               return theMethod.doMethodInvoke(object, EMPTY_ARGUMENTS);
 3445           }
 3446   
 3447           public void setProperty(Object object, Object newValue) {
 3448               throw new UnsupportedOperationException();
 3449           }
 3450       }
 3451   }

Save This Page
Home » groovy-src-1.6.5 » groovy » lang » [javadoc | source]