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

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