http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaClassImpl.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/MetaClassImpl.java 
b/src/main/groovy/groovy/lang/MetaClassImpl.java
new file mode 100644
index 0000000..c453a3f
--- /dev/null
+++ b/src/main/groovy/groovy/lang/MetaClassImpl.java
@@ -0,0 +1,4021 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.lang;
+
+import org.apache.groovy.internal.util.UncheckedThrow;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.reflection.CacheAccessControlException;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedConstructor;
+import org.codehaus.groovy.reflection.CachedField;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ClassInfo;
+import org.codehaus.groovy.reflection.GeneratedMetaMethod;
+import org.codehaus.groovy.reflection.ParameterTypes;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.reflection.android.AndroidSupport;
+import org.codehaus.groovy.runtime.ArrayTypeUtils;
+import org.codehaus.groovy.runtime.ConvertedClosure;
+import org.codehaus.groovy.runtime.CurriedClosure;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.GeneratedClosure;
+import org.codehaus.groovy.runtime.GroovyCategorySupport;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.runtime.MethodClosure;
+import org.codehaus.groovy.runtime.callsite.AbstractCallSite;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.codehaus.groovy.runtime.callsite.ConstructorSite;
+import org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite;
+import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
+import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite;
+import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite;
+import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite;
+import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite;
+import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite;
+import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex;
+import 
org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetBeanMethodMetaProperty;
+import 
org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetMethodMetaProperty;
+import org.codehaus.groovy.runtime.metaclass.MethodSelectionException;
+import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
+import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed;
+import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
+import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.MultipleSetterProperty;
+import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.NewMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.typehandling.NumberMathModificationInfo;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+import org.codehaus.groovy.util.ComplexKeyHashMap;
+import org.codehaus.groovy.util.FastArray;
+import org.codehaus.groovy.util.SingleKeyHashMap;
+import org.objectweb.asm.ClassVisitor;
+
+import java.beans.BeanInfo;
+import java.beans.EventSetDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isDefaultVisibility;
+import static org.codehaus.groovy.reflection.ReflectionCache.isAssignableFrom;
+
+/**
+ * Allows methods to be dynamically added to existing classes at runtime
+ * @see groovy.lang.MetaClass
+ */
+public class MetaClassImpl implements MetaClass, MutableMetaClass {
+
+    public static final Object[] EMPTY_ARGUMENTS = {};
+
+    protected static final String STATIC_METHOD_MISSING = 
"$static_methodMissing";
+    protected static final String STATIC_PROPERTY_MISSING = 
"$static_propertyMissing";
+    protected static final String METHOD_MISSING = "methodMissing";
+    protected static final String PROPERTY_MISSING = "propertyMissing";
+    protected static final String INVOKE_METHOD_METHOD = "invokeMethod";
+
+    private static final String CLOSURE_CALL_METHOD = "call";
+    private static final String CLOSURE_DO_CALL_METHOD = "doCall";
+    private static final String GET_PROPERTY_METHOD = "getProperty";
+    private static final String SET_PROPERTY_METHOD = "setProperty";
+    private static final Class[] METHOD_MISSING_ARGS = new 
Class[]{String.class, Object.class};
+    private static final Class[] GETTER_MISSING_ARGS = new 
Class[]{String.class};
+    private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
+    private static final Comparator<CachedClass> CACHED_CLASS_NAME_COMPARATOR 
= new Comparator<CachedClass>() {
+        public int compare(final CachedClass o1, final CachedClass o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
+    };
+    private static final MetaMethod[] EMPTY = new MetaMethod[0];
+    private static final MetaMethod AMBIGUOUS_LISTENER_METHOD = new 
DummyMetaMethod();
+
+    protected final Class theClass;
+    protected final CachedClass theCachedClass;
+    protected final boolean isGroovyObject;
+    protected final boolean isMap;
+    protected final MetaMethodIndex metaMethodIndex;
+
+    private final Index classPropertyIndex = new MethodIndex();
+    private final SingleKeyHashMap staticPropertyIndex = new 
SingleKeyHashMap();
+    private final Map<String, MetaMethod> listeners = new HashMap<String, 
MetaMethod>();
+    private final List<MetaMethod> allMethods = new ArrayList<MetaMethod>();
+    // we only need one of these that can be reused over and over.
+    private final MetaProperty arrayLengthProperty = new 
MetaArrayLengthProperty();
+    private final Index classPropertyIndexForSuper = new MethodIndex();
+    private final Set<MetaMethod> newGroovyMethodsSet = new 
HashSet<MetaMethod>();
+    private final MetaMethod[] myNewMetaMethods;
+    private final MetaMethod[] additionalMetaMethods;
+
+    protected MetaMethod getPropertyMethod;
+    protected MetaMethod invokeMethodMethod;
+    protected MetaMethod setPropertyMethod;
+    protected MetaClassRegistry registry;
+    private ClassNode classNode;
+    private FastArray constructors;
+    private volatile boolean initialized;
+    private MetaMethod genericGetMethod;
+    private MetaMethod genericSetMethod;
+    private MetaMethod propertyMissingGet;
+    private MetaMethod propertyMissingSet;
+    private MetaMethod methodMissing;
+    private MetaMethodIndex.Header mainClassMethodHeader;
+
+     /**
+      * Constructor
+      *
+      * @param theClass The class this is the metaclass dor
+      * @param add The methods for this class
+      */
+    public MetaClassImpl(final Class theClass, MetaMethod[] add) {
+        this.theClass = theClass;
+        theCachedClass = ReflectionCache.getCachedClass(theClass);
+        this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
+        this.isMap = Map.class.isAssignableFrom(theClass);
+        this.registry = GroovySystem.getMetaClassRegistry();
+        metaMethodIndex = new MetaMethodIndex(theCachedClass);
+        final MetaMethod[] metaMethods = theCachedClass.getNewMetaMethods();
+        if (add != null && !(add.length == 0)) {
+            List<MetaMethod> arr = new ArrayList<MetaMethod>();
+            arr.addAll(Arrays.asList(metaMethods));
+            arr.addAll(Arrays.asList(add));
+            myNewMetaMethods = arr.toArray(new MetaMethod[arr.size()]);
+            additionalMetaMethods = metaMethods;
+        }
+        else {
+            myNewMetaMethods = metaMethods;
+            additionalMetaMethods = EMPTY;
+        }
+    }
+
+    /**
+      * Constructor that sets the methods to null
+      *
+      * @param theClass The class this is the metaclass dor
+      */
+    public MetaClassImpl(final Class theClass) {
+        this(theClass, null);
+    }
+
+    /**
+     * Constructor with registry
+     *
+     * @param registry The metaclass registry for this MetaClass
+     * @param theClass The class
+     * @param add The methods
+     */
+    public MetaClassImpl(MetaClassRegistry registry, final Class theClass, 
MetaMethod add []) {
+        this(theClass, add);
+        this.registry = registry;
+        this.constructors = new FastArray(theCachedClass.getConstructors());
+    }
+
+    /**
+     * Constructor with registry setting methods to null
+     *
+     * @param registry The metaclass registry for this MetaClass
+     * @param theClass The class
+     */
+    public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
+        this(registry, theClass, null);
+    }
+
+    /**
+     * Returns the cached class for this metaclass
+     *
+     * @return The cached class.
+     */
+    public final CachedClass getTheCachedClass() {
+        return theCachedClass;
+    }
+
+    /**
+     * Returns the registry for this metaclass
+     *
+     * @return The resgistry
+     */
+    public MetaClassRegistry getRegistry() {
+        return registry;
+    }
+
+    /**
+     * @see MetaObjectProtocol#respondsTo(Object, String, Object[])
+     */
+    public List respondsTo(Object obj, String name, Object[] argTypes) {
+        Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
+        MetaMethod m = getMetaMethod(name, classes);
+        if (m!=null) {
+            return Collections.singletonList(m);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * @see MetaObjectProtocol#respondsTo(Object, String)
+     */
+    public List respondsTo(final Object obj, final String name) {
+        final Object o = getMethods(getTheClass(), name, false);
+        if (o instanceof FastArray)
+            return ((FastArray) o).toList();
+        else
+            return Collections.singletonList(o);
+    }
+
+    /**
+     * @see MetaObjectProtocol#hasProperty(Object,String)
+     */
+    public MetaProperty hasProperty(Object obj, String name) {
+        return getMetaProperty(name);
+    }
+
+    /**
+     * @see MetaObjectProtocol#getMetaProperty(String)
+     */
+    public MetaProperty getMetaProperty(String name) {
+        SingleKeyHashMap propertyMap = 
classPropertyIndex.getNotNull(theCachedClass);
+        if (propertyMap.containsKey(name)) {
+            return (MetaProperty) propertyMap.get(name);
+        } else if (staticPropertyIndex.containsKey(name)) {
+            return (MetaProperty) staticPropertyIndex.get(name);
+        } else {
+            propertyMap = 
classPropertyIndexForSuper.getNotNull(theCachedClass);
+            if (propertyMap.containsKey(name))
+                return (MetaProperty) propertyMap.get(name);
+            else {
+                CachedClass superClass = theCachedClass;
+                while (superClass != null && superClass != 
ReflectionCache.OBJECT_CLASS) {
+                    final MetaBeanProperty property = 
findPropertyInClassHierarchy(name, superClass);
+                    if (property != null) {
+                        onSuperPropertyFoundInHierarchy(property);
+                        return property;
+                    }
+                    superClass = superClass.getCachedSuperClass();
+                }
+                return null;
+            }
+        }
+    }
+
+    /**
+     * @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
+     */
+    public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) {
+        Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
+        return pickStaticMethod(name, classes);
+    }
+
+
+    /**
+     * @see MetaObjectProtocol#getMetaMethod(String, Object[])
+     */
+    public MetaMethod getMetaMethod(String name, Object[] argTypes) {
+        Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
+        return pickMethod(name, classes);
+    }
+
+    /**
+     *Returns the class this object this is the metaclass of.
+     *
+     * @return The class contained by this metaclass
+     */
+    public Class getTheClass() {
+        return this.theClass;
+    }
+
+    /**
+     * Return wether the class represented by this metaclass instance is an 
instance of the GroovyObject class
+     *
+     * @return true if this is a groovy class, false otherwise.
+     */
+    public boolean isGroovyObject() {
+        return isGroovyObject;
+    }
+
+    /**
+     * Fills the method index
+     */
+    private void fillMethodIndex() {
+        mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
+        LinkedList<CachedClass> superClasses = getSuperClasses();
+        CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
+
+        Set<CachedClass> interfaces = theCachedClass.getInterfaces();
+        addInterfaceMethods(interfaces);
+
+        populateMethods(superClasses, firstGroovySuper);
+
+        inheritInterfaceNewMetaMethods(interfaces);
+        if (isGroovyObject) {
+          metaMethodIndex.copyMethodsToSuper();
+
+          connectMultimethods(superClasses, firstGroovySuper);
+          removeMultimethodsOverloadedWithPrivateMethods();
+
+          replaceWithMOPCalls(theCachedClass.mopMethods);
+        }
+    }
+
+    private void populateMethods(LinkedList<CachedClass> superClasses, 
CachedClass firstGroovySuper) {
+
+        MetaMethodIndex.Header header = 
metaMethodIndex.getHeader(firstGroovySuper.getTheClass());
+        CachedClass c;
+        Iterator<CachedClass> iter = superClasses.iterator();
+        for (; iter.hasNext();) {
+            c = iter.next();
+
+            CachedMethod[] cachedMethods = c.getMethods();
+            for (CachedMethod metaMethod : cachedMethods) {
+                addToAllMethodsIfPublic(metaMethod);
+                if (!metaMethod.isPrivate() || c == firstGroovySuper)
+                    addMetaMethodToIndex(metaMethod, header);
+            }
+
+            MetaMethod[] cachedMethods1 = getNewMetaMethods(c);
+            for (final MetaMethod method : cachedMethods1) {
+                if (!newGroovyMethodsSet.contains(method)) {
+                    newGroovyMethodsSet.add(method);
+                    addMetaMethodToIndex(method, header);
+                }
+            }
+
+            if (c == firstGroovySuper)
+              break;
+        }
+
+        MetaMethodIndex.Header last = header;
+        for (;iter.hasNext();) {
+            c = iter.next();
+            header = metaMethodIndex.getHeader(c.getTheClass());
+
+            if (last != null) {
+                metaMethodIndex.copyNonPrivateMethods(last, header);
+            }
+            last = header;
+
+            for (CachedMethod metaMethod : c.getMethods()) {
+                addToAllMethodsIfPublic(metaMethod);
+                addMetaMethodToIndex(metaMethod, header);
+            }
+
+            for (final MetaMethod method : getNewMetaMethods(c)) {
+                if (method.getName().equals("<init>") && 
!method.getDeclaringClass().equals(theCachedClass)) continue;
+                if (!newGroovyMethodsSet.contains(method)) {
+                    newGroovyMethodsSet.add(method);
+                    addMetaMethodToIndex(method, header);
+                }
+            }
+        }
+    }
+
+    private MetaMethod[] getNewMetaMethods(CachedClass c) {
+        if (theCachedClass != c)
+          return c.getNewMetaMethods();
+
+        return myNewMetaMethods;
+    }
+
+    private void addInterfaceMethods(Set<CachedClass> interfaces) {
+        MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass);
+        for (CachedClass c : interfaces) {
+            final CachedMethod[] m = c.getMethods();
+            for (int i = 0; i != m.length; ++i) {
+                MetaMethod method = m[i];
+                addMetaMethodToIndex(method, header);
+            }
+        }
+    }
+
+    protected LinkedList<CachedClass> getSuperClasses() {
+        LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>();
+
+        if (theClass.isInterface()) {
+            superClasses.addFirst(ReflectionCache.OBJECT_CLASS);
+        } else {
+            for (CachedClass c = theCachedClass; c != null; c = 
c.getCachedSuperClass()) {
+                superClasses.addFirst(c);
+            }
+            if (theCachedClass.isArray && theClass != Object[].class && 
!theClass.getComponentType().isPrimitive()) {
+                superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS);
+            }
+        }
+        return superClasses;
+    }
+
+    private void removeMultimethodsOverloadedWithPrivateMethods() {
+        MethodIndexAction mia = new MethodIndexAction() {
+            public boolean skipClass(Class clazz) {
+                return clazz == theClass;
+            }
+
+            public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) 
{
+                if (e.methods == null)
+                  return;
+
+                boolean hasPrivate = false;
+                if (e.methods instanceof FastArray) {
+                    FastArray methods = (FastArray) e.methods;
+                    final int len = methods.size();
+                    final Object[] data = methods.getArray();
+                    for (int i = 0; i != len; ++i) {
+                        MetaMethod method = (MetaMethod) data[i];
+                        if (method.isPrivate() && clazz == 
method.getDeclaringClass().getTheClass()) {
+                            hasPrivate = true;
+                            break;
+                        }
+                    }
+                }
+                else {
+                    MetaMethod method = (MetaMethod) e.methods;
+                    if (method.isPrivate() && clazz == 
method.getDeclaringClass().getTheClass()) {
+                       hasPrivate = true;
+                    }
+                }
+
+                if (!hasPrivate) return;
+
+                // We have private methods for that name, so remove the
+                // multimethods. That is the same as in our index for
+                // super, so just copy the list from there. It is not
+                // possible to use a pointer here, because the methods
+                // in the index for super are replaced later by MOP
+                // methods like super$5$foo
+                final Object o = e.methodsForSuper;
+                if (o instanceof FastArray)
+                  e.methods = ((FastArray) o).copy();
+                else
+                  e.methods = o;
+            }
+        };
+        mia.iterate();
+    }
+
+
+    private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
+        // no MOP methods if not a child of GroovyObject
+        if (!isGroovyObject) return;
+
+        class MOPIter extends MethodIndexAction {
+            boolean useThis;
+
+            @Override
+            public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) 
{
+                if (useThis) {
+                    if (e.methods == null)
+                      return;
+
+                    if (e.methods instanceof FastArray) {
+                        FastArray methods = (FastArray) e.methods;
+                        processFastArray(methods);
+                    }
+                    else {
+                        MetaMethod method = (MetaMethod) e.methods;
+                        if (method instanceof NewMetaMethod)
+                          return;
+                        if (useThis ^ 
Modifier.isPrivate(method.getModifiers())) return;
+                        String mopName = method.getMopName();
+                        int index = Arrays.binarySearch(mopMethods, mopName, 
CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                        if (index >= 0) {
+                            int from = index;
+                            while (from > 0 && 
mopMethods[from-1].getName().equals(mopName))
+                              from--;
+                            int to = index;
+                            while (to < mopMethods.length-1 && 
mopMethods[to+1].getName().equals(mopName))
+                              to++;
+
+                            int matchingMethod = 
findMatchingMethod(mopMethods, from, to, method);
+                            if (matchingMethod != -1) {
+                                e.methods = mopMethods[matchingMethod];
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (e.methodsForSuper == null)
+                      return;
+
+                    if (e.methodsForSuper instanceof FastArray) {
+                        FastArray methods = (FastArray) e.methodsForSuper;
+                        processFastArray(methods);
+                    }
+                    else {
+                        MetaMethod method = (MetaMethod) e.methodsForSuper;
+                        if (method instanceof NewMetaMethod)
+                          return;
+                        if (useThis ^ 
Modifier.isPrivate(method.getModifiers())) return;
+                        String mopName = method.getMopName();
+                        // GROOVY-4922: Due to a numbering scheme change, we 
must find the super$X$method which exists
+                        // with the highest number. If we don't, no method may 
be found, leading to a stack overflow
+                        String[] decomposedMopName = decomposeMopName(mopName);
+                        int distance = Integer.parseInt(decomposedMopName[1]);
+                        while (distance>0) {
+                            String fixedMopName = decomposedMopName[0] + 
distance + decomposedMopName[2];
+                            int index = Arrays.binarySearch(mopMethods, 
fixedMopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                            if (index >= 0) {
+                                int from = index;
+                                while (from > 0 && 
mopMethods[from-1].getName().equals(fixedMopName))
+                                  from--;
+                                int to = index;
+                                while (to < mopMethods.length-1 && 
mopMethods[to+1].getName().equals(fixedMopName))
+                                  to++;
+
+                                int matchingMethod = 
findMatchingMethod(mopMethods, from, to, method);
+                                if (matchingMethod != -1) {
+                                    e.methodsForSuper = 
mopMethods[matchingMethod];
+                                    distance = 0;
+                                }
+                            }
+                            distance--;
+                        }
+                    }
+                }
+            }
+
+            private String[] decomposeMopName(final String mopName) {
+                int idx = mopName.indexOf("$");
+                if (idx>0) {
+                    int eidx = mopName.indexOf("$", idx+1);
+                    if (eidx>0) {
+                        return new String[] {
+                                mopName.substring(0, idx+1),
+                                mopName.substring(idx+1, eidx),
+                                mopName.substring(eidx)
+                        };
+                    }
+                }
+                return new String[]{"","0",mopName};
+            }
+
+            private void processFastArray(FastArray methods) {
+                final int len = methods.size();
+                final Object[] data = methods.getArray();
+                for (int i = 0; i != len; ++i) {
+                    MetaMethod method = (MetaMethod) data[i];
+                    if (method instanceof NewMetaMethod) continue;
+                    boolean isPrivate = 
Modifier.isPrivate(method.getModifiers());
+                    if (useThis ^ isPrivate) continue;
+                    String mopName = method.getMopName();
+                    int index = Arrays.binarySearch(mopMethods, mopName, 
CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                    if (index >= 0) {
+                        int from = index;
+                        while (from > 0 && 
mopMethods[from-1].getName().equals(mopName))
+                          from--;
+                        int to = index;
+                        while (to < mopMethods.length-1 && 
mopMethods[to+1].getName().equals(mopName))
+                          to++;
+
+                        int matchingMethod = findMatchingMethod(mopMethods, 
from, to, method);
+                        if (matchingMethod != -1) {
+                            methods.set(i, mopMethods[matchingMethod]);
+                        }
+                    }
+                }
+            }
+        }
+        MOPIter iter = new MOPIter();
+
+        // replace all calls for super with the correct MOP method
+        iter.useThis = false;
+        iter.iterate();
+        // replace all calls for this with the correct MOP method
+        iter.useThis = true;
+        iter.iterate();
+    }
+
+    private void inheritInterfaceNewMetaMethods(Set<CachedClass> interfaces) {
+        // add methods declared by DGM for interfaces
+        for (CachedClass cls : interfaces) {
+            MetaMethod methods[] = getNewMetaMethods(cls);
+            for (MetaMethod method : methods) {
+                boolean skip = false;
+                // skip DGM methods on an interface if the class already has 
the method
+                // but don't skip for GroovyObject-related methods as it 
breaks things :-(
+                if (method instanceof GeneratedMetaMethod && 
!isAssignableFrom(GroovyObject.class, 
method.getDeclaringClass().getTheClass())) {
+                    for (Method m : theClass.getMethods()) {
+                        if (method.getName().equals(m.getName())
+                                // below not true for DGM#push and also 
co-variant return scenarios
+                                //&& 
method.getReturnType().equals(m.getReturnType())
+                                && 
MetaMethod.equal(method.getParameterTypes(), m.getParameterTypes())) {
+                            skip = true;
+                            break;
+                        }
+                    }
+                }
+                if (!skip) {
+                    if (!newGroovyMethodsSet.contains(method)) {
+                        newGroovyMethodsSet.add(method);
+                    }
+                    addMetaMethodToIndex(method, mainClassMethodHeader);
+                }
+            }
+        }
+    }
+
+    private void connectMultimethods(List<CachedClass> superClasses, 
CachedClass firstGroovyClass) {
+        superClasses = DefaultGroovyMethods.reverse(superClasses);
+        MetaMethodIndex.Header last = null;
+        for (final CachedClass c : superClasses) {
+            MetaMethodIndex.Header methodIndex = 
metaMethodIndex.getHeader(c.getTheClass());
+            // We don't copy DGM methods to superclasses' indexes
+            // The reason we can do that is particular set of DGM methods in 
use,
+            // if at some point we will define DGM method for some Groovy 
class or
+            // for a class derived from such, we will need to revise this 
condition.
+            // It saves us a lot of space and some noticeable time
+            if (last != null) 
metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex);
+            last = methodIndex;
+
+            if (c == firstGroovyClass)
+                break;
+        }
+    }
+
+    private CachedClass calcFirstGroovySuperClass(Collection superClasses) {
+        if (theCachedClass.isInterface)
+          return ReflectionCache.OBJECT_CLASS;
+
+        CachedClass firstGroovy = null;
+        Iterator iter = superClasses.iterator();
+        for (; iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            if (GroovyObject.class.isAssignableFrom(c.getTheClass())) {
+              firstGroovy = c;
+              break;
+            }
+        }
+
+        if (firstGroovy == null)
+          firstGroovy = theCachedClass;
+        else {
+            if (firstGroovy.getTheClass() == GroovyObjectSupport.class && 
iter.hasNext()) {
+                firstGroovy = (CachedClass) iter.next();
+                if (firstGroovy.getTheClass() == Closure.class && 
iter.hasNext()) {
+                    firstGroovy = (CachedClass) iter.next();
+                }
+            }
+        }
+
+        return GroovyObject.class.isAssignableFrom(firstGroovy.getTheClass()) 
? firstGroovy.getCachedSuperClass() : firstGroovy;
+    }
+
+    /**
+     * Gets all instance methods available on this class for the given name
+     *
+     * @return all the normal instance methods available on this class for the
+     *         given name
+     */
+    private Object getMethods(Class sender, String name, boolean 
isCallToSuper) {
+        Object answer;
+
+        final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, 
name);
+        if (entry == null)
+            answer = FastArray.EMPTY_LIST;
+        else
+            if (isCallToSuper) {
+                answer = entry.methodsForSuper;
+            } else {
+                answer = entry.methods;
+            }
+
+        if (answer == null) answer = FastArray.EMPTY_LIST;
+
+        if (!isCallToSuper) {
+            List used = GroovyCategorySupport.getCategoryMethods(name);
+            if (used != null) {
+                FastArray arr;
+                if (answer instanceof MetaMethod) {
+                    arr = new FastArray();
+                    arr.add(answer);
+                }
+                else
+                    arr = ((FastArray) answer).copy();
+
+                for (Iterator iter = used.iterator(); iter.hasNext();) {
+                    MetaMethod element = (MetaMethod) iter.next();
+                    if 
(!element.getDeclaringClass().getTheClass().isAssignableFrom(sender))
+                      continue;
+                    filterMatchingMethodForCategory(arr, element);
+                }
+                answer = arr;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Returns all the normal static methods on this class for the given name
+     *
+     * @return all the normal static methods available on this class for the
+     *         given name
+     */
+    private Object getStaticMethods(Class sender, String name) {
+        final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, 
name);
+        if (entry == null)
+            return FastArray.EMPTY_LIST;
+        Object answer = entry.staticMethods;
+        if (answer == null)
+            return FastArray.EMPTY_LIST;
+        return answer;
+    }
+
+    /**
+     * Returns whether this MetaClassImpl has been modified. Since 
MetaClassImpl
+     * is not designed for modification this method always returns false
+     *
+     * @return false
+     */
+    public boolean isModified() {
+        return false;  // MetaClassImpl not designed for modification, just 
return false
+    }
+
+    /**
+     *Adds an instance method to this metaclass.
+     *
+     * @param method The method to be added
+     */
+    public void addNewInstanceMethod(Method method) {
+        final CachedMethod cachedMethod = CachedMethod.find(method);
+        NewInstanceMetaMethod newMethod = new 
NewInstanceMetaMethod(cachedMethod);
+        final CachedClass declaringClass = newMethod.getDeclaringClass();
+        addNewInstanceMethodToIndex(newMethod, 
metaMethodIndex.getHeader(declaringClass.getTheClass()));
+    }
+
+    private void addNewInstanceMethodToIndex(MetaMethod newMethod, 
MetaMethodIndex.Header header) {
+        if (!newGroovyMethodsSet.contains(newMethod)) {
+            newGroovyMethodsSet.add(newMethod);
+            addMetaMethodToIndex(newMethod, header);
+        }
+    }
+
+    /**
+     *Adds a static method to this metaclass.
+     *
+     * @param method The method to be added
+     */
+    public void addNewStaticMethod(Method method) {
+        final CachedMethod cachedMethod = CachedMethod.find(method);
+        NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
+        final CachedClass declaringClass = newMethod.getDeclaringClass();
+        addNewStaticMethodToIndex(newMethod, 
metaMethodIndex.getHeader(declaringClass.getTheClass()));
+    }
+
+    private void addNewStaticMethodToIndex(MetaMethod newMethod, 
MetaMethodIndex.Header header) {
+        if (!newGroovyMethodsSet.contains(newMethod)) {
+            newGroovyMethodsSet.add(newMethod);
+            addMetaMethodToIndex(newMethod, header);
+        }
+    }
+
+    /**
+     * Invoke a method on the given object with the given arguments.
+     *
+     * @param object The object the method should be invoked on.
+     * @param methodName The name of the method to invoke.
+     * @param arguments The arguments to the invoked method as null, a Tuple, 
an array or a single argument of any type.
+     *
+     * @return The result of the method invocation.
+     */
+    public Object invokeMethod(Object object, String methodName, Object 
arguments) {
+        if (arguments == null) {
+            return invokeMethod(object, methodName, 
MetaClassHelper.EMPTY_ARRAY);
+        }
+        if (arguments instanceof Tuple) {
+            Tuple tuple = (Tuple) arguments;
+            return invokeMethod(object, methodName, tuple.toArray());
+        }
+        if (arguments instanceof Object[]) {
+            return invokeMethod(object, methodName, (Object[]) arguments);
+        } else {
+            return invokeMethod(object, methodName, new Object[]{arguments});
+        }
+    }
+
+    /**
+     * Invoke a missing method on the given object with the given arguments.
+     *
+     * @param instance The object the method should be invoked on.
+     * @param methodName The name of the method to invoke.
+     * @param arguments The arguments to the invoked method.
+     *
+     * @return The result of the method invocation.
+     */
+    public Object invokeMissingMethod(Object instance, String methodName, 
Object[] arguments) {
+        return invokeMissingMethod(instance, methodName, arguments, null, 
false);
+    }
+
+    /**
+     * Invoke a missing property on the given object with the given arguments.
+     *
+     * @param instance The object the method should be invoked on.
+     * @param propertyName The name of the property to invoke.
+     * @param optionalValue The (optional) new value for the property
+     * @param isGetter Wether the method is a getter
+     *
+     * @return The result of the method invocation.
+     */
+    public Object invokeMissingProperty(Object instance, String propertyName, 
Object optionalValue, boolean isGetter) {
+        Class theClass = instance instanceof Class ? (Class)instance : 
instance.getClass();
+        CachedClass superClass = theCachedClass;
+        while(superClass != null && superClass != 
ReflectionCache.OBJECT_CLASS) {
+            final MetaBeanProperty property = 
findPropertyInClassHierarchy(propertyName, superClass);
+            if(property != null) {
+                onSuperPropertyFoundInHierarchy(property);
+                if(!isGetter) {
+                    property.setProperty(instance, optionalValue);
+                    return null;
+                }
+                else {
+                    return property.getProperty(instance);
+                }
+            }
+            superClass = superClass.getCachedSuperClass();
+        }
+        // got here to property not found, look for getProperty or setProperty 
overrides
+        if(isGetter) {
+            final Class[] getPropertyArgs = {String.class};
+            final MetaMethod method = 
findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD, 
getPropertyArgs, this);
+            if(method != null && method instanceof ClosureMetaMethod) {
+                onGetPropertyFoundInHierarchy(method);
+                return method.invoke(instance,new Object[]{propertyName});
+            }
+        }
+        else {
+            final Class[] setPropertyArgs = {String.class, Object.class};
+            final MetaMethod method = 
findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD, 
setPropertyArgs, this);
+            if(method != null && method instanceof ClosureMetaMethod) {
+                onSetPropertyFoundInHierarchy(method);
+                return method.invoke(instance, new Object[]{propertyName, 
optionalValue});
+            }
+        }
+
+        try {
+            if (!(instance instanceof Class)) {
+                if (isGetter) {
+                    if (propertyMissingGet != null) {
+                        return propertyMissingGet.invoke(instance, new 
Object[]{propertyName});
+                    }
+                } else {
+                    if (propertyMissingSet != null) {
+                        return propertyMissingSet.invoke(instance, new 
Object[]{propertyName, optionalValue});
+                    }
+                }
+            }
+        } catch (InvokerInvocationException iie) {
+            boolean shouldHandle = isGetter && propertyMissingGet != null;
+            if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet 
!= null;
+            if (shouldHandle &&  iie.getCause() instanceof 
MissingPropertyException) {
+                throw (MissingPropertyException) iie.getCause();
+            }
+            throw iie;
+        }
+
+        if (instance instanceof Class && theClass != Class.class) {
+           final MetaProperty metaProperty = 
InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName);
+           if (metaProperty != null)
+             if (isGetter)
+               return metaProperty.getProperty(instance);
+             else {
+               metaProperty.setProperty(instance, optionalValue);
+               return null;
+             }
+        }
+        throw new MissingPropertyExceptionNoStack(propertyName, theClass);
+    }
+
+    private Object invokeMissingMethod(Object instance, String methodName, 
Object[] arguments, RuntimeException original, boolean isCallToSuper) {
+        if (!isCallToSuper) {
+            Class instanceKlazz = instance.getClass();
+            if (theClass != instanceKlazz && 
theClass.isAssignableFrom(instanceKlazz))
+              instanceKlazz = theClass;
+
+            Class[] argClasses = 
MetaClassHelper.castArgumentsToClassArray(arguments);
+
+            MetaMethod method = findMixinMethod(methodName, argClasses);
+            if(method != null) {
+                onMixinMethodFound(method);
+                return method.invoke(instance, arguments);
+            }
+
+            method = findMethodInClassHierarchy(instanceKlazz, methodName, 
argClasses, this);
+            if(method != null) {
+                onSuperMethodFoundInHierarchy(method);
+                return method.invoke(instance, arguments);
+            }
+
+            // still not method here, so see if there is an invokeMethod 
method up the hierarchy
+            final Class[] invokeMethodArgs = {String.class, Object[].class};
+            method = findMethodInClassHierarchy(instanceKlazz, 
INVOKE_METHOD_METHOD, invokeMethodArgs, this );
+            if(method != null && method instanceof ClosureMetaMethod) {
+                onInvokeMethodFoundInHierarchy(method);
+                return method.invoke(instance, invokeMethodArgs);
+            }
+        }
+
+        if (methodMissing != null) {
+            try {
+                return methodMissing.invoke(instance, new Object[]{methodName, 
arguments});
+            } catch (InvokerInvocationException iie) {
+                if (methodMissing instanceof ClosureMetaMethod && 
iie.getCause() instanceof MissingMethodException) {
+                    MissingMethodException mme =  (MissingMethodException) 
iie.getCause();
+                    throw new MissingMethodExecutionFailed(mme.getMethod(), 
mme.getClass(),
+                                                            
mme.getArguments(),mme.isStatic(),mme);
+                }
+                throw iie;
+            } catch (MissingMethodException mme) {
+                if (methodMissing instanceof ClosureMetaMethod)
+                    throw new MissingMethodExecutionFailed(mme.getMethod(), 
mme.getClass(),
+                                                        
mme.getArguments(),mme.isStatic(),mme);
+                else
+                    throw mme;
+            }
+        } else if (original != null) throw original;
+        else throw new MissingMethodExceptionNoStack(methodName, theClass, 
arguments, false);
+    }
+
+    protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) {
+    }
+
+    protected void onMixinMethodFound(MetaMethod method) {
+    }
+
+    protected void onSuperMethodFoundInHierarchy(MetaMethod method) {
+    }
+
+    protected void onInvokeMethodFoundInHierarchy(MetaMethod method) {
+    }
+
+    protected void onSetPropertyFoundInHierarchy(MetaMethod method) {
+    }
+
+    protected void onGetPropertyFoundInHierarchy(MetaMethod method) {
+    }
+
+
+    /**
+     * Hook to deal with the case of MissingProperty for static properties. 
The method will look attempt to look up
+     * "propertyMissing" handlers and invoke them otherwise thrown a 
MissingPropertyException
+     *
+     * @param instance      The instance
+     * @param propertyName  The name of the property
+     * @param optionalValue The value in the case of a setter
+     * @param isGetter      True if its a getter
+     * @return The value in the case of a getter or a MissingPropertyException
+     */
+    protected Object invokeStaticMissingProperty(Object instance, String 
propertyName, Object optionalValue, boolean isGetter) {
+        MetaClass mc = instance instanceof Class ? 
registry.getMetaClass((Class) instance) : this;
+        if (isGetter) {
+            MetaMethod propertyMissing = 
mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
+            if (propertyMissing != null) {
+                return propertyMissing.invoke(instance, new 
Object[]{propertyName});
+            }
+        } else {
+            MetaMethod propertyMissing = 
mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
+            if (propertyMissing != null) {
+                return propertyMissing.invoke(instance, new 
Object[]{propertyName, optionalValue});
+            }
+        }
+
+        if (instance instanceof Class) {
+            throw new MissingPropertyException(propertyName, (Class) instance);
+        }
+        throw new MissingPropertyException(propertyName, theClass);
+    }
+
+
+    /**
+     * Invokes a method on the given receiver for the specified arguments.
+     * The MetaClass will attempt to establish the method to invoke based on 
the name and arguments provided.
+     *
+     *
+     * @param object The object which the method was invoked on
+     * @param methodName The name of the method
+     * @param originalArguments The arguments to the method
+     *
+     * @return The return value of the method
+     *
+     * @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, 
boolean)
+     */
+    public Object invokeMethod(Object object, String methodName, Object[] 
originalArguments) {
+        return invokeMethod(theClass, object, methodName, originalArguments, 
false, false);
+    }
+
+    private Object invokeMethodClosure(Object object, String methodName, 
Object[] arguments) {
+        final MethodClosure mc = (MethodClosure) object;
+        final Object owner = mc.getOwner();
+
+        methodName = mc.getMethod();
+        final Class ownerClass = owner instanceof Class ? (Class) owner : 
owner.getClass();
+        final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
+
+        // To conform to "Least Surprise" principle, try to invoke method with 
original arguments first, which can match most of use cases
+        try {
+            return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, 
arguments, false, false);
+        } catch (MissingMethodExceptionNoStack e) {
+            // CONSTRUCTOR REFERENCE
+            if (owner instanceof Class && 
MethodClosure.NEW.equals(methodName)) {
+                if (ownerClass.isArray()) {
+                    if (0 == arguments.length) {
+                        throw new GroovyRuntimeException("The 
arguments(specifying size) are required to create array[" + 
ownerClass.getCanonicalName() + "]");
+                    }
+
+                    int arrayDimension = ArrayTypeUtils.dimension(ownerClass);
+
+                    if (arguments.length > arrayDimension) {
+                        throw new GroovyRuntimeException("The length[" + 
arguments.length + "] of arguments should not be greater than the dimensions[" 
+ arrayDimension + "] of array[" + ownerClass.getCanonicalName() + "]");
+                    }
+
+                    int[] sizeArray = new int[arguments.length];
+
+                    for (int i = 0, n = sizeArray.length; i < n; i++) {
+                        Object argument = arguments[i];
+
+                        if (argument instanceof Integer) {
+                            sizeArray[i] = (Integer) argument;
+                        } else {
+                            sizeArray[i] = 
Integer.parseInt(String.valueOf(argument));
+                        }
+                    }
+
+                    Class arrayType =
+                            arguments.length == arrayDimension
+                                    ? ArrayTypeUtils.elementType(ownerClass) 
// Just for better performance, though we can use reduceDimension only
+                                    : 
ArrayTypeUtils.reduceDimension(ownerClass, (arrayDimension - arguments.length));
+                    return Array.newInstance(arrayType, sizeArray);
+                }
+
+                return ownerMetaClass.invokeConstructor(arguments);
+            }
+
+            // METHOD REFERENCE
+            // if and only if the owner is a class and the method closure can 
be related to some instance methods,
+            // try to invoke method with adjusted arguments(first argument is 
the actual owner) again.
+            // otherwise throw the MissingMethodExceptionNoStack.
+            if (!(owner instanceof Class
+                    && ((Boolean) 
mc.getProperty(MethodClosure.ANY_INSTANCE_METHOD_EXISTS)).booleanValue())) {
+
+                throw e;
+            }
+
+            if (arguments.length <= 0) {
+                return invokeMissingMethod(object, methodName, arguments);
+            }
+
+            Object newOwner = arguments[0];
+            Object[] newArguments = Arrays.copyOfRange(arguments, 1, 
arguments.length);
+            return ownerMetaClass.invokeMethod(ownerClass, newOwner, 
methodName, newArguments, false, false);
+        }
+    }
+
+    /**
+     * <p>Invokes a method on the given receiver for the specified arguments. 
The sender is the class that invoked the method on the object.
+     * The MetaClass will attempt to establish the method to invoke based on 
the name and arguments provided.
+     *
+     * <p>The isCallToSuper and fromInsideClass help the Groovy runtime 
perform optimisations on the call to go directly
+     * to the super class if necessary
+     *
+     * @param sender The java.lang.Class instance that invoked the method
+     * @param object The object which the method was invoked on
+     * @param methodName The name of the method
+     * @param originalArguments The arguments to the method
+     * @param isCallToSuper Whether the method is a call to a super class 
method
+     * @param fromInsideClass Whether the call was invoked from the inside or 
the outside of the class
+     *
+     * @return The return value of the method
+     *
+     * @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, 
boolean)
+     */
+    public Object invokeMethod(Class sender, Object object, String methodName, 
Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+        checkInitalised();
+        if (object == null) {
+            throw new NullPointerException("Cannot invoke method: " + 
methodName + " on null object");
+        }
+
+        final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS 
: originalArguments;
+//        final Class[] argClasses = 
MetaClassHelper.convertToTypeArray(arguments);
+//
+//        unwrap(arguments);
+
+        MetaMethod method = null;
+        if (CLOSURE_CALL_METHOD.equals(methodName) && object instanceof 
GeneratedClosure) {
+            method = getMethodWithCaching(sender, "doCall", arguments, 
isCallToSuper);
+        }
+        if (method==null) {
+            method = getMethodWithCaching(sender, methodName, arguments, 
isCallToSuper);
+        }
+        MetaClassHelper.unwrap(arguments);
+
+        if (method == null)
+            method = tryListParamMetaMethod(sender, methodName, isCallToSuper, 
arguments);
+
+        final boolean isClosure = object instanceof Closure;
+        if (isClosure) {
+            final Closure closure = (Closure) object;
+            final Object owner = closure.getOwner();
+
+            if (CLOSURE_CALL_METHOD.equals(methodName) || 
CLOSURE_DO_CALL_METHOD.equals(methodName)) {
+                final Class objectClass = object.getClass();
+                if (objectClass == MethodClosure.class) {
+                    return this.invokeMethodClosure(object, methodName, 
arguments);
+                } else if (objectClass == CurriedClosure.class) {
+                    final CurriedClosure cc = (CurriedClosure) object;
+                    // change the arguments for an uncurried call
+                    final Object[] curriedArguments = 
cc.getUncurriedArguments(arguments);
+                    final Class ownerClass = owner instanceof Class ? (Class) 
owner : owner.getClass();
+                    final MetaClass ownerMetaClass = 
registry.getMetaClass(ownerClass);
+                    return ownerMetaClass.invokeMethod(owner, methodName, 
curriedArguments);
+                }
+                if (method==null) 
invokeMissingMethod(object,methodName,arguments);
+            }
+
+            final Object delegate = closure.getDelegate();
+            final boolean isClosureNotOwner = owner != closure;
+            final int resolveStrategy = closure.getResolveStrategy();
+
+            final Class[] argClasses = 
MetaClassHelper.convertToTypeArray(arguments);
+
+            switch (resolveStrategy) {
+                case Closure.TO_SELF:
+                    method = closure.getMetaClass().pickMethod(methodName, 
argClasses);
+                    if (method != null) return method.invoke(closure, 
arguments);
+                    break;
+                case Closure.DELEGATE_ONLY:
+                    if (method == null && delegate != closure && delegate != 
null) {
+                        MetaClass delegateMetaClass = 
lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, 
argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, 
methodName, originalArguments);
+                        else if (delegate != closure && (delegate instanceof 
GroovyObject)) {
+                            return invokeMethodOnGroovyObject(methodName, 
originalArguments, delegate);
+                        }
+                    }
+                    break;
+                case Closure.OWNER_ONLY:
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = 
lookupObjectMetaClass(owner);
+                        return ownerMetaClass.invokeMethod(owner, methodName, 
originalArguments);
+                    }
+                    break;
+                case Closure.DELEGATE_FIRST:
+                    if (method == null && delegate != closure && delegate != 
null) {
+                        MetaClass delegateMetaClass = 
lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, 
argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, 
methodName, originalArguments);
+                    }
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = 
lookupObjectMetaClass(owner);
+                        method = ownerMetaClass.pickMethod(methodName, 
argClasses);
+                        if (method != null) return 
ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
+                    }
+                    if (method == null && resolveStrategy != Closure.TO_SELF) {
+                        // still no methods found, test if delegate or owner 
are GroovyObjects
+                        // and invoke the method on them if so.
+                        MissingMethodException last = null;
+                        if (delegate != closure && (delegate instanceof 
GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, 
originalArguments, delegate);
+                            } catch (MissingMethodException mme) {
+                                if (last == null) last = mme;
+                            }
+                        }
+                        if (isClosureNotOwner && (owner instanceof 
GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, 
originalArguments, owner);
+                            } catch (MissingMethodException mme) {
+                                last = mme;
+                            }
+                        }
+                        if (last != null) return invokeMissingMethod(object, 
methodName, originalArguments, last, isCallToSuper);
+                    }
+
+                    break;
+                default:
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = 
lookupObjectMetaClass(owner);
+                        method = ownerMetaClass.pickMethod(methodName, 
argClasses);
+                        if (method != null) return 
ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
+                    }
+                    if (method == null && delegate != closure && delegate != 
null) {
+                        MetaClass delegateMetaClass = 
lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, 
argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, 
methodName, originalArguments);
+                    }
+                    if (method == null && resolveStrategy != Closure.TO_SELF) {
+                        // still no methods found, test if delegate or owner 
are GroovyObjects
+                        // and invoke the method on them if so.
+                        MissingMethodException last = null;
+                        if (isClosureNotOwner && (owner instanceof 
GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, 
originalArguments, owner);
+                            } catch (MissingMethodException mme) {
+                                if (methodName.equals(mme.getMethod())) {
+                                    if (last == null) last = mme;
+                                } else {
+                                    throw mme;
+                                }
+                            }
+                            catch (InvokerInvocationException iie) {
+                                if (iie.getCause() instanceof 
MissingMethodException) {
+                                    MissingMethodException mme = 
(MissingMethodException) iie.getCause();
+                                    if (methodName.equals(mme.getMethod())) {
+                                        if (last == null) last = mme;
+                                    } else {
+                                        throw iie;
+                                    }
+                                }
+                                else
+                                  throw iie;
+                            }
+                        }
+                        if (delegate != closure && (delegate instanceof 
GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, 
originalArguments, delegate);
+                            } catch (MissingMethodException mme) {
+                                last = mme;
+                            }
+                            catch (InvokerInvocationException iie) {
+                                if (iie.getCause() instanceof 
MissingMethodException) {
+                                    last = (MissingMethodException) 
iie.getCause();
+                                }
+                                else
+                                  throw iie;
+                            }
+                        }
+                        if (last != null) return invokeMissingMethod(object, 
methodName, originalArguments, last, isCallToSuper);
+                    }
+            }
+        }
+
+        if (method != null) {
+            return method.doMethodInvoke(object, arguments);
+        } else {
+            return invokePropertyOrMissing(object, methodName, 
originalArguments, fromInsideClass, isCallToSuper);
+        }
+    }
+
+    private MetaMethod tryListParamMetaMethod(Class sender, String methodName, 
boolean isCallToSuper, Object[] arguments) {
+        MetaMethod method = null;
+        if (arguments.length == 1 && arguments[0] instanceof List) {
+            Object[] newArguments = ((List) arguments[0]).toArray();
+            method = createTransformMetaMethod(getMethodWithCaching(sender, 
methodName, newArguments, isCallToSuper));
+        }
+        return method;
+    }
+
+    protected MetaMethod createTransformMetaMethod(MetaMethod method) {
+        if (method == null) {
+            return null;
+        }
+
+        return new TransformMetaMethod(method) {
+            public Object invoke(Object object, Object[] arguments) {
+                Object firstArgument = arguments[0];
+                List list = (List) firstArgument;
+                arguments = list.toArray();
+                return super.invoke(object, arguments);
+            }
+        };
+    }
+
+    private Object invokePropertyOrMissing(Object object, String methodName, 
Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
+        // if no method was found, try to find a closure defined as a field of 
the class and run it
+        Object value = null;
+        final MetaProperty metaProperty = this.getMetaProperty(methodName, 
false);
+        if (metaProperty != null)
+          value = metaProperty.getProperty(object);
+        else {
+            if (object instanceof Map)
+              value = ((Map)object).get(methodName);
+        }
+
+        if (value instanceof Closure) {  // This test ensures that value != 
this If you ever change this ensure that value != this
+            Closure closure = (Closure) value;
+            MetaClass delegateMetaClass = closure.getMetaClass();
+            return delegateMetaClass.invokeMethod(closure.getClass(), closure, 
CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
+        }
+
+        if (object instanceof Script) {
+            Object bindingVar = ((Script) 
object).getBinding().getVariables().get(methodName);
+            if (bindingVar != null) {
+                MetaClass bindingVarMC = ((MetaClassRegistryImpl) 
registry).getMetaClass(bindingVar);
+                return bindingVarMC.invokeMethod(bindingVar, 
CLOSURE_CALL_METHOD, originalArguments);
+            }
+        }
+        return invokeMissingMethod(object, methodName, originalArguments, 
null, isCallToSuper);
+    }
+
+    private MetaClass lookupObjectMetaClass(Object object) {
+        if (object instanceof GroovyObject) {
+            GroovyObject go = (GroovyObject) object;
+            return go.getMetaClass();
+        }
+        Class ownerClass = object.getClass();
+        if (ownerClass == Class.class) ownerClass = (Class) object;
+        MetaClass metaClass = registry.getMetaClass(ownerClass);
+        return metaClass;
+    }
+
+    private static Object invokeMethodOnGroovyObject(String methodName, 
Object[] originalArguments, Object owner) {
+        GroovyObject go = (GroovyObject) owner;
+        return go.invokeMethod(methodName, originalArguments);
+    }
+
+    public MetaMethod getMethodWithCaching(Class sender, String methodName, 
Object[] arguments, boolean isCallToSuper) {
+        // let's try use the cache to find the method
+        if (!isCallToSuper && 
GroovyCategorySupport.hasCategoryInCurrentThread()) {
+            return getMethodWithoutCaching(sender, methodName, 
MetaClassHelper.convertToTypeArray(arguments), isCallToSuper);
+        } else {
+            final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, 
methodName);
+            if (e == null)
+              return null;
+
+            return isCallToSuper ? getSuperMethodWithCaching(arguments, e) : 
getNormalMethodWithCaching(arguments, e);
+        }
+    }
+
+    private static boolean sameClasses(Class[] params, Class[] arguments) {
+        // we do here a null check because the params field might not have 
been set yet
+        if (params == null) return false;
+
+        if (params.length != arguments.length)
+            return false;
+
+        for (int i = params.length - 1; i >= 0; i--) {
+            Object arg = arguments[i];
+            if (arg != null) {
+                if (params[i] != arguments[i]) return false;
+            } else return false;
+        }
+
+        return true;
+    }
+
+    // This method should be called by CallSite only
+    private MetaMethod getMethodWithCachingInternal (Class sender, CallSite 
site, Class [] params) {
+        if (GroovyCategorySupport.hasCategoryInCurrentThread())
+            return getMethodWithoutCaching(sender, site.getName (), params, 
false);
+
+        final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, 
site.getName());
+        if (e == null) {
+            return null;
+        }
+
+        MetaMethodIndex.CacheEntry cacheEntry;
+        final Object methods = e.methods;
+        if (methods == null)
+          return null;
+
+        cacheEntry = e.cachedMethod;
+        if (cacheEntry != null && (sameClasses(cacheEntry.params, params))) {
+             return cacheEntry.method;
+        }
+
+        cacheEntry = new MetaMethodIndex.CacheEntry (params, (MetaMethod) 
chooseMethod(e.name, methods, params));
+        e.cachedMethod = cacheEntry;
+        return cacheEntry.method;
+    }
+
+    private MetaMethod getSuperMethodWithCaching(Object[] arguments, 
MetaMethodIndex.Entry e) {
+        MetaMethodIndex.CacheEntry cacheEntry;
+        if (e.methodsForSuper == null)
+          return null;
+
+        cacheEntry = e.cachedMethodForSuper;
+
+        if (cacheEntry != null &&
+            MetaClassHelper.sameClasses(cacheEntry.params, arguments, 
e.methodsForSuper instanceof MetaMethod))
+        {
+            MetaMethod method = cacheEntry.method;
+            if (method!=null) return method;
+        }
+
+        final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
+        MetaMethod method = (MetaMethod) chooseMethod(e.name, 
e.methodsForSuper, classes);
+        cacheEntry = new MetaMethodIndex.CacheEntry (classes, 
method.isAbstract()?null:method);
+
+        e.cachedMethodForSuper = cacheEntry;
+
+        return cacheEntry.method;
+    }
+
+    private MetaMethod getNormalMethodWithCaching(Object[] arguments, 
MetaMethodIndex.Entry e) {
+        MetaMethodIndex.CacheEntry cacheEntry;
+        final Object methods = e.methods;
+        if (methods == null)
+          return null;
+
+        cacheEntry = e.cachedMethod;
+
+        if (cacheEntry != null &&
+            MetaClassHelper.sameClasses(cacheEntry.params, arguments, methods 
instanceof MetaMethod))
+        {
+            MetaMethod method = cacheEntry.method;
+            if (method!=null) return method;
+        }
+
+        final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
+        cacheEntry = new MetaMethodIndex.CacheEntry (classes, (MetaMethod) 
chooseMethod(e.name, methods, classes));
+
+        e.cachedMethod = cacheEntry;
+
+        return cacheEntry.method;
+    }
+
+    public Constructor retrieveConstructor(Class[] arguments) {
+        CachedConstructor constructor = (CachedConstructor) 
chooseMethod("<init>", constructors, arguments);
+        if (constructor != null) {
+            return constructor.cachedConstructor;
+        }
+        constructor = (CachedConstructor) chooseMethod("<init>", constructors, 
arguments);
+        if (constructor != null) {
+            return constructor.cachedConstructor;
+        }
+        return null;
+    }
+
+    public MetaMethod retrieveStaticMethod(String methodName, Object[] 
arguments) {
+        final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(theClass, 
methodName);
+        MetaMethodIndex.CacheEntry cacheEntry;
+        if (e != null) {
+            cacheEntry = e.cachedStaticMethod;
+
+            if (cacheEntry != null &&
+                MetaClassHelper.sameClasses(cacheEntry.params, arguments, 
e.staticMethods instanceof MetaMethod))
+            {
+                 return cacheEntry.method;
+            }
+
+            final Class[] classes = 
MetaClassHelper.convertToTypeArray(arguments);
+            cacheEntry = new MetaMethodIndex.CacheEntry (classes, 
pickStaticMethod(methodName, classes));
+
+            e.cachedStaticMethod = cacheEntry;
+
+            return cacheEntry.method;
+        }
+        else
+          return pickStaticMethod(methodName, 
MetaClassHelper.convertToTypeArray(arguments));
+    }
+
+    public MetaMethod getMethodWithoutCaching(Class sender, String methodName, 
Class[] arguments, boolean isCallToSuper) {
+        MetaMethod method = null;
+        Object methods = getMethods(sender, methodName, isCallToSuper);
+        if (methods != null) {
+            method = (MetaMethod) chooseMethod(methodName, methods, arguments);
+        }
+        return method;
+    }
+
+    public Object invokeStaticMethod(Object object, String methodName, 
Object[] arguments) {
+        checkInitalised();
+
+        final Class sender = object instanceof Class ? (Class) object : 
object.getClass();
+        if (sender != theClass) {
+            MetaClass mc = registry.getMetaClass(sender);
+            return mc.invokeStaticMethod(sender, methodName, arguments);
+        }
+        if (sender == Class.class) {
+            return invokeMethod(object, methodName, arguments);
+        }
+
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+//        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+
+        MetaMethod method = retrieveStaticMethod(methodName, arguments);
+        // let's try use the cache to find the method
+
+        if (method != null) {
+            MetaClassHelper.unwrap(arguments);
+            return method.doMethodInvoke(object, arguments);
+        }
+        Object prop = null;
+        try {
+            prop = getProperty(theClass, theClass, methodName, false, false);
+        } catch (MissingPropertyException mpe) {
+            // ignore
+        }
+
+        if (prop instanceof Closure) {
+            return invokeStaticClosureProperty(arguments, prop);
+        }
+
+        Object[] originalArguments = arguments.clone();
+        MetaClassHelper.unwrap(arguments);
+
+        Class superClass = sender.getSuperclass();
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        while (superClass != Object.class && superClass != null) {
+            MetaClass mc = registry.getMetaClass(superClass);
+            method = mc.getStaticMetaMethod(methodName, argClasses);
+            if (method != null) return method.doMethodInvoke(object, 
arguments);
+
+            try {
+                prop = mc.getProperty(superClass, superClass, methodName, 
false, false);
+            } catch (MissingPropertyException mpe) {
+                // ignore
+            }
+
+            if (prop instanceof Closure) {
+                return invokeStaticClosureProperty(originalArguments, prop);
+            }
+
+            superClass = superClass.getSuperclass();
+        }
+
+        if (prop != null) {
+            MetaClass propMC = registry.getMetaClass(prop.getClass());
+            return propMC.invokeMethod(prop, CLOSURE_CALL_METHOD, arguments);
+        }
+
+        return invokeStaticMissingMethod(sender, methodName, arguments);
+    }
+
+    private static Object invokeStaticClosureProperty(Object[] 
originalArguments, Object prop) {
+        Closure closure = (Closure) prop;
+        MetaClass delegateMetaClass = closure.getMetaClass();
+        return delegateMetaClass.invokeMethod(closure.getClass(), closure, 
CLOSURE_DO_CALL_METHOD, originalArguments, false, false);
+    }
+
+    private Object invokeStaticMissingMethod(Class sender, String methodName, 
Object[] arguments) {
+        MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, 
METHOD_MISSING_ARGS);
+        if (metaMethod != null) {
+            return metaMethod.invoke(sender, new Object[]{methodName, 
arguments});
+        }
+        throw new MissingMethodException(methodName, sender, arguments, true);
+    }
+
+    private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
+        MetaMethod method = null;
+        MethodSelectionException mse = null;
+        Object methods = getStaticMethods(theClass, methodName);
+
+        if (!(methods instanceof FastArray) || 
!((FastArray)methods).isEmpty()) {
+            try {
+                method = (MetaMethod) chooseMethod(methodName, methods, 
arguments);
+            } catch(MethodSelectionException msex) {
+                mse = msex;
+            }
+        }
+        if (method == null && theClass != Class.class) {
+            MetaClass classMetaClass = registry.getMetaClass(Class.class);
+            method = classMetaClass.pickMethod(methodName, arguments);
+        }
+        if (method == null) {
+            method = (MetaMethod) chooseMethod(methodName, methods, 
MetaClassHelper.convertToTypeArray(arguments));
+        }
+
+        if (method == null && mse != null) {
+            throw mse;
+        } else {
+            return method;
+        }
+    }
+
+    public Object invokeConstructor(Object[] arguments) {
+        return invokeConstructor(theClass, arguments);
+    }
+
+    public int selectConstructorAndTransformArguments(int 
numberOfConstructors, Object[] arguments) {
+        if (numberOfConstructors==-1) {
+            return selectConstructorAndTransformArguments1(arguments);
+        } else {
+            // falling back to pre 2.1.9 selection algorithm
+            // in practice this branch will only be reached if the class 
calling this code is a Groovy class
+            // compiled with an earlier version of the Groovy compiler
+            return 
selectConstructorAndTransformArguments0(numberOfConstructors, arguments);
+        }
+
+
+    }
+
+    private int selectConstructorAndTransformArguments0(final int 
numberOfConstructors, Object[] arguments) {
+        //TODO: that is just a quick prototype, not the real thing!
+        if (numberOfConstructors != constructors.size()) {
+            throw new IncompatibleClassChangeError("the number of constructors 
during runtime and compile time for " +
+                    this.theClass.getName() + " do not match. Expected " + 
numberOfConstructors + " but got " + constructors.size());
+        }
+
+        CachedConstructor constructor = createCachedConstructor(arguments);
+        List l = new ArrayList(constructors.toList());
+        Comparator comp = new Comparator() {
+            public int compare(Object arg0, Object arg1) {
+                CachedConstructor c0 = (CachedConstructor) arg0;
+                CachedConstructor c1 = (CachedConstructor) arg1;
+                String descriptor0 = 
BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes());
+                String descriptor1 = 
BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes());
+                return descriptor0.compareTo(descriptor1);
+            }
+        };
+        Collections.sort(l, comp);
+        int found = -1;
+        for (int i = 0; i < l.size(); i++) {
+            if (l.get(i) != constructor) continue;
+            found = i;
+            break;
+        }
+        // NOTE: must be changed to "1 |" if constructor was vargs
+        return 0 | (found << 8);
+    }
+
+    private CachedConstructor createCachedConstructor(Object[] arguments) {
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        MetaClassHelper.unwrap(arguments);
+        CachedConstructor constructor = (CachedConstructor) 
chooseMethod("<init>", constructors, argClasses);
+        if (constructor == null) {
+            constructor = (CachedConstructor) chooseMethod("<init>", 
constructors, argClasses);
+        }
+        if (constructor == null) {
+            throw new GroovyRuntimeException(
+                    "Could not find matching constructor for: "
+                            + theClass.getName()
+                            + "(" + InvokerHelper.toTypeString(arguments) + 
")");
+        }
+        return constructor;
+    }
+
+    /**
+     * Constructor selection algorithm for Groovy 2.1.9+.
+     * This selection algorithm was introduced as a workaround for 
GROOVY-6080. Instead of generating an index between
+     * 0 and N where N is the number of super constructors at the time the 
class is compiled, this algorithm uses
+     * a hash of the constructor descriptor instead.
+     *
+     * This has the advantage of letting the super class add new constructors 
while being binary compatible. But there
+     * are still problems with this approach:
+     * <ul>
+     *     <li>There's a risk of hash collision, even if it's very low (two 
constructors of the same class must have the same hash)</li>
+     *     <li>If the super class adds a new constructor which takes as an 
argument a superclass of an existing constructor parameter and
+     *     that this new constructor is selected at runtime, it would not find 
it.</li>
+     * </ul>
+     *
+     * Hopefully in the last case, the error message is much nicer now since 
it explains that it's a binary incompatible change.
+     *
+     * @param arguments the actual constructor call arguments
+     * @return a hash used to identify the constructor to be called
+     * @since 2.1.9
+     */
+    private int selectConstructorAndTransformArguments1(Object[] arguments) {
+        CachedConstructor constructor = createCachedConstructor(arguments);
+        final String methodDescriptor = 
BytecodeHelper.getMethodDescriptor(Void.TYPE, 
constructor.getNativeParameterTypes());
+        // keeping 3 bits for additional information such as vargs
+        return BytecodeHelper.hashCode(methodDescriptor);
+    }
+
+
+    /**
+     * checks if the initialisation of the class id complete.
+     * This method should be called as a form of assert, it is no
+     * way to test if there is still initialisation work to be done.
+     * Such logic must be implemented in a different way.
+     *
+     * @throws IllegalStateException if the initialisation is incomplete yet
+     */
+    protected void checkInitalised() {
+        if (!isInitialized())
+            throw new IllegalStateException(
+                    "initialize must be called for meta " +
+                            "class of " + theClass +
+                            "(" + this.getClass() + ") " +
+                            "to complete initialisation process " +
+                            "before any invocation or field/property " +
+                            "access can be done");
+    }
+
+    /**
+     * This is a helper class introduced in Groovy 2.1.0, which is used only by
+     * indy. This class is for internal use only.
+     * @since Groovy 2.1.0
+     */
+    public static final class MetaConstructor extends MetaMethod {
+        private final CachedConstructor cc;
+        private final boolean beanConstructor;
+        private MetaConstructor(CachedConstructor cc, boolean bean) {
+            super(cc.getNativeParameterTypes());
+            this.setParametersTypes(cc.getParameterTypes());
+            this.cc = cc;
+            this.beanConstructor = bean;
+        }
+        @Override
+        public int getModifiers() { return cc.getModifiers(); }
+        @Override
+        public String getName() { return "<init>"; }
+        @Override
+        public Class getReturnType() { return 
cc.getCachedClass().getTheClass(); }
+        @Override
+        public CachedClass getDeclaringClass() { return cc.getCachedClass(); }
+        @Override
+        public Object invoke(Object object, Object[] arguments) {
+            return cc.doConstructorInvoke(arguments);
+        }
+        public CachedConstructor getCachedConstrcutor() { return cc; }
+        public boolean isBeanConstructor() { return beanConstructor; }
+    }
+
+    /**
+     * This is a helper method added in Groovy 2.1.0, which is used only by 
indy.
+     * This method is for internal use only.
+     * @since Groovy 2.1.0
+     */
+    public MetaMethod retrieveConstructor(Object[] arguments) {
+        checkInitalised();
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        MetaClassHelper.unwrap(arguments);
+        Object res = chooseMethod("<init>", constructors, argClasses);
+        if (res instanceof MetaMethod) return (MetaMethod) res;
+        CachedConstructor constructor = (CachedConstructor) res;
+        if (constructor != null) return new MetaConstructor(constructor, 
false);
+        if (arguments.length == 1 && arguments[0] instanceof Map) {
+            res = chooseMethod("<init>", constructors, 
MetaClassHelper.EMPTY_TYPE_ARRAY);
+        } else if (
+                arguments.length == 2 && arguments[1] instanceof Map &&
+                theClass.getEnclosingClass()!=null &&
+                theClass.getEnclosingClass().isAssignableFrom(argClasses[0]))
+        {
+            res = chooseMethod("<init>", constructors, new 
Class[]{argClasses[0]});
+        }
+        if (res instanceof MetaMethod) return (MetaMethod) res;
+        constructor = (CachedConstructor) res;
+        if (constructor != null) return new MetaConstructor(constructor, true);
+
+        return null;
+    }
+
+    private Object invokeConstructor(Class at, Object[] arguments) {
+        checkInitalised();
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        MetaClassHelper.unwrap(arguments);
+        CachedConstructor constructor = (CachedConstructor) 
chooseMethod("<init>", constructors, argClasses);
+        if (constructor != null) {
+            return constructor.doConstructorInvoke(arguments);
+        }
+
+        if (arguments.length == 1) {
+            Object firstArgument = arguments[0];
+            if (firstArgument instanceof Map) {
+                constructor = (CachedConstructor) chooseMethod("<init>", 
constructors, MetaClassHelper.EMPTY_TYPE_ARRAY);
+                if (constructor != null) {
+                    Object bean = 
constructor.doConstructorInvoke(MetaClassHelper.EMPTY_ARRAY);
+                    setProperties(bean, ((Map) firstArgument));
+                    return bean;
+                }
+            }
+        }
+        throw new GroovyRuntimeException(
+                "Could not find matching constructor for: "
+                        + theClass.getName()
+                        + "(" + InvokerHelper.toTypeString(arguments) + ")");
+    }
+
+    /**
+     * Sets a number of bean properties from the given Map where the keys are
+     * the String names of properties and the values are the values of the
+     * properties to set
+     */
+    public void setProperties(Object bean, Map map) {
+        checkInitalised();
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String key = entry.getKey().toString();
+
+            Object value = entry.getValue();
+            setProperty(bean, key, value);
+        }
+    }
+
+    /**
+     * @return the given property's value on the object
+     */
+    public Object getProperty(Class sender, Object object, String name, 
boolean useSuper, boolean fromInsideClass) {
+
+        
//----------------------------------------------------------------------
+        // handling of static
+        
//----------------------------------------------------------------------
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            MetaClass mc = registry.getMetaClass((Class) object);
+            return mc.getProperty(sender, object, name, useSuper, false);
+        }
+
+        checkInitalised();
+
+        
//----------------------------------------------------------------------
+        // turn getProperty on a Map to get on the Map itself
+        
//----------------------------------------------------------------------
+        if (!isStatic && this.isMap) {
+            return ((Map) object).get(name);
+        }
+
+        Tuple2<MetaMethod, MetaProperty> methodAndProperty = 
createMetaMethodAndMetaProperty(sender, sender, name, useSuper, isStatic);
+        MetaMethod method = methodAndProperty.getFirst();
+
+        
//----------------------------------------------------------------------
+        // getter
+        
//----------------------------------------------------------------------
+        MetaProperty mp = methodAndProperty.getSecond();
+
+        
//----------------------------------------------------------------------
+        // field
+        
//----------------------------------------------------------------------
+        if (method == null && mp != null) {
+            try {
+                return mp.getProperty(object);
+            } catch (IllegalArgumentException e) {
+                // can't access the field directly but there may be a getter
+                mp = null;
+            } catch (CacheAccessControlException e) {
+                // can't access the field directly but there may be a getter
+                mp = null;
+            }
+        }
+
+        
//----------------------------------------------------------------------
+        // generic get method
+        
//----------------------------------------------------------------------
+        // check for a generic get method provided through a category
+        Object[] arguments = EMPTY_ARGUMENTS;
+        if (method == null && !useSuper && !isStatic && 
GroovyCategorySupport.hasCategoryInCurrentThread()) {
+            method = getCategoryMethodGetter(sender, "get", true);
+            if (method != null) arguments = new Object[]{name};
+        }
+
+        // the generic method is valid, if available (!=null), if static or
+        // if it is not static and we do no static access
+        if (method == null && genericGetMethod != null && 
!(!genericGetMethod.isStatic() && isStatic)) {
+            arguments = new Object[]{name};
+            method = genericGetMethod;
+        }
+
+        
//----------------------------------------------------------------------
+        // special cases
+        
//----------------------------------------------------------------------
+        if (method == null) {
+            /** todo these special cases should be special MetaClasses maybe */
+            if (theClass != Class.class && object instanceof Class) {
+                MetaClass mc = registry.getMetaClass(Class.class);
+                return mc.getProperty(Class.class, object, name, useSuper, 
false);
+            } else if (object instanceof Collection) {
+                return DefaultGroovyMethods.getAt((Collection) object, name);
+            } else if (object instanceof Object[]) {
+                return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) 
object), name);
+            } else {
+                MetaMethod addListenerMethod = listeners.get(name);
+                if (addListenerMethod != null) {
+                    //TODO: one day we could try return the previously 
registered Closure listener for easy removal
+                    return null;
+                }
+            }
+        } else {
+
+            
//----------------------------------------------------------------------
+            // executing the getter method
+            
//----------------------------------------------------------------------
+            return method.doMethodInvoke(object, arguments);
+        }
+
+        
//----------------------------------------------------------------------
+        // error due to missing method/field
+        
//----------------------------------------------------------------------
+        if (isStatic || object instanceof Class)
+            return invokeStaticMissingProperty(object, name, null, true);
+        else
+            return invokeMissingProperty(object, name, null, true);
+    }
+
+    public MetaProperty getEffectiveGetMetaProperty(final Class sender, final 
Object object, String name, final boolean useSuper) {
+
+        
//----------------------------------------------------------------------
+        // handling of static
+        
//----------------------------------------------------------------------
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            return new MetaProperty(name, Object.class) {
+                final MetaClass mc = registry.getMetaClass((Class) object);
+
+                public Object getProperty(Object object) {
+                    return mc.getProperty(sender, object, name, 
useSuper,false);
+                }

<TRUNCATED>

Reply via email to