You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 00:56:37 UTC

[40/49] groovy git commit: Move source files to proper packages

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ExpandoMetaClass.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ExpandoMetaClass.java b/src/main/groovy/groovy/lang/ExpandoMetaClass.java
new file mode 100644
index 0000000..d56f738
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ExpandoMetaClass.java
@@ -0,0 +1,1510 @@
+/*
+ *  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.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.MixinInMetaClass;
+import org.codehaus.groovy.runtime.DefaultCachedMethodKey;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.runtime.MethodKey;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.codehaus.groovy.runtime.callsite.ConstructorMetaMethodSite;
+import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
+import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite;
+import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite;
+import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.ClosureStaticMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.DefaultMetaClassInfo;
+import org.codehaus.groovy.runtime.metaclass.MethodSelectionException;
+import org.codehaus.groovy.runtime.metaclass.MixedInMetaClass;
+import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.OwnedMetaClass;
+import org.codehaus.groovy.runtime.metaclass.ThreadManagedMetaBeanProperty;
+import org.codehaus.groovy.util.FastArray;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * ExpandoMetaClass is a MetaClass that behaves like an Expando, allowing the addition or replacement
+ * of methods, properties and constructors on the fly.
+ * <p>
+ * Some examples of usage:
+ * <pre>
+ * // defines or replaces instance method:
+ * metaClass.myMethod = { args -> }
+ *
+ * // defines a new instance method
+ * metaClass.myMethod << { args -> }
+ *
+ * // creates multiple overloaded methods of the same name
+ * metaClass.myMethod << { String s -> } << { Integer i -> }
+ *
+ * // defines or replaces a static method with the 'static' qualifier
+ * metaClass.'static'.myMethod = { args ->  }
+ *
+ * // defines a new static method with the 'static' qualifier
+ * metaClass.'static'.myMethod << { args ->  }
+ *
+ * // defines a new constructor
+ * metaClass.constructor << { String arg -> }
+ *
+ * // defines or replaces a constructor
+ * metaClass.constructor = { String arg -> }
+ *
+ * // defines a new property with an initial value of "blah"
+ * metaClass.myProperty = "blah"
+ * </pre>
+ * <p>
+ * ExpandoMetaClass also supports a DSL/builder like notation to combine multiple definitions together. So instead of this:
+ * <pre>
+ * Number.metaClass.multiply = { Amount amount -> amount.times(delegate) }
+ * Number.metaClass.div =      { Amount amount -> amount.inverse().times(delegate) }
+ * </pre>
+ * You can also now do this:
+ * <pre>
+ * Number.metaClass {
+ *     multiply { Amount amount -> amount.times(delegate) }
+ *     div      { Amount amount -> amount.inverse().times(delegate) }
+ * }
+ * </pre>
+ * <p>
+ * ExpandoMetaClass also supports runtime mixins. While {@code @Mixin} allows you to mix in new behavior
+ * to classes you own and are designing, you can not easily mixin anything to types you didn't own, e.g.
+ * from third party libraries or from JDK library classes.
+ * Runtime mixins let you add a mixin on any type at runtime.
+ * <pre>
+ * interface Vehicle {
+ *     String getName()
+ * }
+ *
+ * // Category annotation style
+ * {@code @Category}(Vehicle) class FlyingAbility {
+ *     def fly() { "I'm the ${name} and I fly!" }
+ * }
+ *
+ * // traditional category style
+ * class DivingAbility {
+ *     static dive(Vehicle self) { "I'm the ${self.name} and I dive!" }
+ * }
+ *
+ * // provided by a third-party, so can't augment using Mixin annotation
+ * class JamesBondVehicle implements Vehicle {
+ *     String getName() { "James Bond's vehicle" }
+ * }
+ *
+ * // Can be added via metaClass, e.g.:
+ * // JamesBondVehicle.metaClass.mixin DivingAbility, FlyingAbility
+ * // Or using shorthand through DGM method on Class
+ * JamesBondVehicle.mixin DivingAbility, FlyingAbility
+ *
+ * assert new JamesBondVehicle().fly() ==
+ *        "I'm the James Bond's vehicle and I fly!"
+ * assert new JamesBondVehicle().dive() ==
+ *        "I'm the James Bond's vehicle and I dive!"
+ * </pre>
+ * As another example, consider the following class definitions:
+ * <pre>
+ * class Student {
+ *     List<String> schedule = []
+ *     def addLecture(String lecture) { schedule << lecture }
+ * }
+ *
+ * class Worker {
+ *     List<String> schedule = []
+ *     def addMeeting(String meeting) { schedule << meeting }
+ * }
+ * </pre>
+ * We can mimic a form of multiple inheritance as follows:
+ * <pre>
+ * class CollegeStudent {
+ *     static { mixin Student, Worker }
+ * }
+ * new CollegeStudent().with {
+ *     addMeeting('Performance review with Boss')
+ *     addLecture('Learn about Groovy Mixins')
+ *     println schedule
+ *     println mixedIn[Student].schedule
+ *     println mixedIn[Worker].schedule
+ * }
+ * </pre>
+ * Which outputs these lines when run:
+ * <pre>
+ * [Performance review with Boss]
+ * [Learn about Groovy Mixins]
+ * [Performance review with Boss]
+ * </pre>
+ * Perhaps some explanation is required here. The methods and properties of Student and Worker are
+ * added to CollegeStudent. Worker is added last, so for overlapping methods, its methods will
+ * be used, e.g. when calling <code>schedule</code>, it will be the schedule property (getSchedule method)
+ * from Worker that is used. The schedule property from Student will be shadowed but the <code>mixedIn</code>
+ * notation allows us to get to that too if we need as the last two lines show.
+ * <p>
+ * We can also be a little more dynamic and not require the CollegeStudent class to
+ * be defined at all, e.g.:
+ * <pre>
+ * def cs = new Object()
+ * cs.metaClass {
+ *     mixin Student, Worker
+ *     getSchedule {
+ *         mixedIn[Student].schedule + mixedIn[Worker].schedule
+ *     }
+ * }
+ * cs.with {
+ *     addMeeting('Performance review with Boss')
+ *     addLecture('Learn about Groovy Mixins')
+ *     println schedule
+ * }
+ * </pre>
+ * Which outputs this line when run:
+ * <pre>
+ * [Learn about Groovy Mixins, Performance review with Boss]
+ * </pre>
+ * As another example, we can also define a no dup queue by mixing in some
+ * Queue and Set functionality as follows:
+ * <pre>
+ * def ndq = new Object()
+ * ndq.metaClass {
+ *     mixin ArrayDeque
+ *     mixin HashSet
+ *     leftShift = { Object o ->
+ *         if (!mixedIn[Set].contains(o)) {
+ *             mixedIn[Queue].push(o)
+ *             mixedIn[Set].add(o)
+ *         }
+ *     }
+ * }
+ * ndq << 1
+ * ndq << 2
+ * ndq << 1
+ * assert ndq.size() == 2
+ * </pre>
+ * As a final example, we sometimes need to pass such mixed in classes or objects
+ * into Java methods which require a given static type but the ExpandoMetaClass mixin approach uses a very dynamic
+ * approach based on duck typing rather than static interface definitions, so doesn't by default
+ * produce objects matching the required static type. Luckily, there is a mixins capability
+ * within ExpandoMetaClass which supports the use of Groovy's common 'as StaticType' notation to produce an object
+ * having the correct static type so that it can be passed to the Java method call in question.
+ * A slightly contrived example illustrating this feature:
+ * <pre>
+ * class CustomComparator implements Comparator {
+ *     int compare(Object a, b) { return a.size() - b.size() }
+ * }
+ *
+ * class CustomCloseable implements Closeable {
+ *     void close() { println 'Lights out - I am closing' }
+ * }
+ *
+ * import static mypackage.IOUtils.closeQuietly
+ * import static java.util.Collections.sort
+ * def o = new Object()
+ * o.metaClass.mixin CustomComparator, CustomCloseable
+ * def items = ['a', 'bbb', 'cc']
+ * sort(items, o as Comparator)
+ * println items                // => [a, cc, bbb]
+ * closeQuietly(o as Closeable) // => Lights out - I am closing
+ * </pre>
+ * <p>
+ * <b>Further details</b>
+ * <p>
+ * When using the default implementations of MetaClass, methods are only allowed to be added before initialize() is called.
+ * In other words you create a new MetaClass, add some methods and then call initialize(). If you attempt to add new methods
+ * after initialize() has been called, an error will be thrown. This is to ensure that the MetaClass can operate appropriately
+ * in multi-threaded environments as it forces you to do all method additions at the beginning, before using the MetaClass.
+ * <p>
+ * ExpandoMetaClass differs here from the default in that it allows you to add methods after initialize has been called.
+ * This is done by setting the initialize flag internally to false and then add the methods. Since this is not thread
+ * safe it has to be done in a synchronized block. The methods to check for modification and initialization are
+ * therefore synchronized as well. Any method call done through this meta class will first check if the it is
+ * synchronized. Should this happen during a modification, then the method cannot be selected or called unless the
+ * modification is completed.
+ * <p>
+ *
+ * @author Graeme Rocher
+ * @since 1.5
+ */
+public class ExpandoMetaClass extends MetaClassImpl implements GroovyObject {
+
+    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+    private static final String META_CLASS = "metaClass";
+    private static final String CLASS = "class";
+    private static final String META_METHODS = "metaMethods";
+    private static final String METHODS = "methods";
+    private static final String PROPERTIES = "properties";
+    public static final String STATIC_QUALIFIER = "static";
+    public static final String CONSTRUCTOR = "constructor";
+
+    private static final String CLASS_PROPERTY = "class";
+    private static final String META_CLASS_PROPERTY = "metaClass";
+    private static final String GROOVY_CONSTRUCTOR = "<init>";
+
+    // These two properties are used when no ExpandoMetaClassCreationHandle is present
+
+    private MetaClass myMetaClass;
+    private boolean initialized;
+    private volatile boolean modified;
+
+    private boolean initCalled;
+    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+    private final Lock readLock = rwl.readLock();
+    private final Lock writeLock = rwl.writeLock();
+    
+    private final boolean allowChangesAfterInit;
+    public boolean inRegistry;
+    
+    private final Set<MetaMethod> inheritedMetaMethods = new HashSet<MetaMethod>();
+    private final Map<String, MetaProperty> beanPropertyCache = new ConcurrentHashMap<String, MetaProperty>(16, 0.75f, 1);
+    private final Map<String, MetaProperty> staticBeanPropertyCache = new ConcurrentHashMap<String, MetaProperty>(16, 0.75f, 1);
+    private final Map<MethodKey, MetaMethod> expandoMethods = new ConcurrentHashMap<MethodKey, MetaMethod>(16, 0.75f, 1);
+
+    public Collection getExpandoSubclassMethods() {
+        return expandoSubclassMethods.values();
+    }
+
+    private final ConcurrentHashMap expandoSubclassMethods = new ConcurrentHashMap(16, 0.75f, 1);
+    private final Map<String, MetaProperty> expandoProperties = new ConcurrentHashMap<String, MetaProperty>(16, 0.75f, 1);
+    private ClosureStaticMetaMethod invokeStaticMethodMethod;
+    private final Set<MixinInMetaClass> mixinClasses = new LinkedHashSet<MixinInMetaClass>();
+
+    public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add) {
+        this(GroovySystem.getMetaClassRegistry(), theClass, register, allowChangesAfterInit, add);
+    }
+    
+    public ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add) {
+        super(registry, theClass, add);
+        this.myMetaClass = InvokerHelper.getMetaClass(getClass());
+        this.inRegistry = register;
+        this.allowChangesAfterInit = allowChangesAfterInit;
+    }
+    
+    /**
+     * Constructs a new ExpandoMetaClass instance for the given class
+     *
+     * @param theClass The class that the MetaClass applies to
+     */
+    public ExpandoMetaClass(Class theClass) {
+        this(theClass,false,false,null);
+    }
+
+    public ExpandoMetaClass(Class theClass, MetaMethod [] add) {
+        this(theClass,false,false,add);
+    }
+
+    /**
+     * Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass
+     * in the MetaClassRegistry automatically
+     *
+     * @param theClass The class that the MetaClass applies to
+     * @param register True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
+     */
+    public ExpandoMetaClass(Class theClass, boolean register) {
+        this(theClass,register,false,null);
+    }
+
+    public ExpandoMetaClass(Class theClass, boolean register, MetaMethod [] add) {
+        this(theClass, register, false, add);
+    }
+
+    /**
+     * Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass
+     * in the MetaClassRegistry automatically
+     *
+     * @param theClass The class that the MetaClass applies to
+     * @param register True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
+     * @param allowChangesAfterInit Should the meta class be modifiable after initialization. Default is false.
+     */
+    public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit) {
+        this(theClass, register, allowChangesAfterInit, null);
+    }
+
+    public MetaMethod findMixinMethod(String methodName, Class[] arguments) {
+        for (MixinInMetaClass mixin : mixinClasses) {
+            final CachedClass mixinClass = mixin.getMixinClass();
+            MetaClass metaClass = mixinClass.classInfo.getMetaClassForClass();
+            if (metaClass == null) {
+                metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(mixinClass.getTheClass());
+            }
+
+            MetaMethod metaMethod = metaClass.pickMethod(methodName, arguments);
+            if (metaMethod == null && metaClass instanceof MetaClassImpl) {
+                MetaClassImpl mc = (MetaClassImpl) metaClass;
+                for (CachedClass cl = mc.getTheCachedClass().getCachedSuperClass(); cl != null; cl = cl.getCachedSuperClass()) {
+                    metaMethod = mc.getMethodWithoutCaching(cl.getTheClass(), methodName, arguments, false);
+                    if (metaMethod != null)
+                        break;
+                }
+            }
+            if (metaMethod != null) {
+                MetaMethod method = new MixinInstanceMetaMethod(metaMethod, mixin);
+
+                if (method.getParameterTypes().length == 1 && !method.getParameterTypes()[0].isPrimitive) {
+                    MetaMethod noParam = pickMethod(methodName, EMPTY_CLASS_ARRAY);
+                    // if the current call itself is with empty arg class array, no need to recurse with 'new Class[0]'
+                    if (noParam == null && arguments.length != 0) {
+                        try {
+                            findMixinMethod(methodName, EMPTY_CLASS_ARRAY);
+                        } catch (MethodSelectionException msex) {
+                            /*
+                             * Here we just additionally tried to find another no-arg mixin method of the same name and register that as well, if found.
+                             * Safe to ignore a MethodSelectionException in this additional exercise. (GROOVY-4999)
+                             */
+                        }
+                    }
+                }
+
+                registerInstanceMethod(method);
+                return method;
+            }
+        }
+        return null;
+    }
+
+    protected void onInvokeMethodFoundInHierarchy(MetaMethod method) {
+        this.invokeMethodMethod = method;
+    }
+
+    protected void onSuperMethodFoundInHierarchy(MetaMethod method) {
+        addSuperMethodIfNotOverridden(method);
+    }
+
+    protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) {
+        addMetaBeanProperty(property);
+    }
+
+    protected void onSetPropertyFoundInHierarchy(MetaMethod method) {
+        this.setPropertyMethod = method;
+    }
+
+    protected void onGetPropertyFoundInHierarchy(MetaMethod method) {
+        this.getPropertyMethod = method;
+    }
+
+    public boolean isModified() {
+        return this.modified;
+    }
+
+    public void registerSubclassInstanceMethod(String name, Class klazz, Closure closure) {
+        final List<MetaMethod> list = ClosureMetaMethod.createMethodList(name, klazz, closure);
+        for (MetaMethod metaMethod : list) {
+            registerSubclassInstanceMethod(metaMethod);
+        }
+    }
+
+    public void registerSubclassInstanceMethod(MetaMethod metaMethod) {
+        modified = true;
+
+        final String name = metaMethod.getName();
+        Object methodOrList = expandoSubclassMethods.get(name);
+        if (methodOrList == null) {
+            expandoSubclassMethods.put(name, metaMethod);
+        } else {
+            if (methodOrList instanceof MetaMethod) {
+                FastArray arr = new FastArray(2);
+                arr.add(methodOrList);
+                arr.add(metaMethod);
+                expandoSubclassMethods.put(name, arr);
+            } else {
+                ((FastArray) methodOrList).add(metaMethod);
+            }
+        }
+    }
+
+    public void addMixinClass(MixinInMetaClass mixin) {
+        mixinClasses.add(mixin);
+    }
+
+    public Object castToMixedType(Object obj, Class type) {
+        for (MixinInMetaClass mixin : mixinClasses) {
+            if (type.isAssignableFrom(mixin.getMixinClass().getTheClass()))
+                return mixin.getMixinInstance(obj);
+        }
+        return null;
+    }
+
+    /**
+     * For simulating closures in Java
+     */
+    private interface Callable {
+        void call();
+    }
+
+    /**
+     * Call to enable global use of ExpandoMetaClass within the registry.
+     * This has the advantage that inheritance will function correctly and 
+     * metaclass modifications will also apply to existing objects,
+     * but has a higher memory usage on the JVM than normal Groovy
+     */
+    public static void enableGlobally() {
+        DefaultMetaClassInfo.setWithoutCustomMetaclassCreationHandle(false);
+        ExpandoMetaClassCreationHandle.enable();
+    }
+
+    /**
+     * Call to disable the global use of ExpandoMetaClass
+     */
+    public static void disableGlobally() {
+        DefaultMetaClassInfo.setWithoutCustomMetaclassCreationHandle(true);
+        ExpandoMetaClassCreationHandle.disable();
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClassImpl#initialize()
+     */
+
+    public void initialize() {
+        try {
+            writeLock.lock();
+            if (!isInitialized()) {
+                super.initialize();
+                setInitialized(true);
+                this.initCalled = true;
+            }
+        } finally {
+            // downgrade to readlock before releasing just in case
+            readLock.lock();
+            writeLock.unlock();
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Checks if the meta class is initialized.
+     * @see groovy.lang.MetaClassImpl#isInitialized()
+     */
+    protected boolean isInitialized() {
+        try {
+            readLock.lock();
+            return this.initialized;
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    protected void setInitialized(boolean b) {
+        this.initialized = b;
+    }
+
+    private void addSuperMethodIfNotOverridden(final MetaMethod metaMethodFromSuper) {
+        performOperationOnMetaClass(new Callable() {
+            public void call() {
+
+                MetaMethod existing = null;
+                try {
+                    existing = pickMethod(metaMethodFromSuper.getName(), metaMethodFromSuper.getNativeParameterTypes());
+                } catch ( GroovyRuntimeException e) {
+                    // ignore, this happens with overlapping method definitions
+                }
+
+                if (existing == null) {
+                    addMethodWithKey(metaMethodFromSuper);
+                } else {
+                    boolean isGroovyMethod = getMetaMethods().contains(existing);
+
+                    if (isGroovyMethod) {
+                        addMethodWithKey(metaMethodFromSuper);
+                    } else if (inheritedMetaMethods.contains(existing)) {
+                        inheritedMetaMethods.remove(existing);
+                        addMethodWithKey(metaMethodFromSuper);
+                    }
+                }
+            }
+
+            private void addMethodWithKey(final MetaMethod metaMethodFromSuper) {
+                inheritedMetaMethods.add(metaMethodFromSuper);
+                if (metaMethodFromSuper instanceof ClosureMetaMethod) {
+                    ClosureMetaMethod closureMethod = (ClosureMetaMethod)metaMethodFromSuper;
+                    String name = metaMethodFromSuper.getName();
+                    final Class declaringClass = metaMethodFromSuper.getDeclaringClass().getTheClass();
+                    ClosureMetaMethod localMethod = ClosureMetaMethod.copy(closureMethod);
+                    addMetaMethod(localMethod);
+
+                    MethodKey key = new DefaultCachedMethodKey(declaringClass, name, localMethod.getParameterTypes(), false);
+
+                    checkIfGroovyObjectMethod(localMethod);
+                    expandoMethods.put(key, localMethod);
+
+                }
+            }
+        });
+    }
+
+    /**
+     * Instances of this class are returned when using the << left shift operator.
+     * <p>
+     * Example:
+     * <p>
+     * metaClass.myMethod << { String args -> }
+     * <p>
+     * This allows callbacks to the ExpandoMetaClass for registering appending methods
+     *
+     * @author Graeme Rocher
+     */
+    protected class ExpandoMetaProperty extends GroovyObjectSupport {
+
+        protected String propertyName;
+        protected boolean isStatic;
+
+        protected ExpandoMetaProperty(String name) {
+            this(name, false);
+        }
+
+        protected ExpandoMetaProperty(String name, boolean isStatic) {
+            this.propertyName = name;
+            this.isStatic = isStatic;
+        }
+
+        public String getPropertyName() {
+            return this.propertyName;
+        }
+
+        public boolean isStatic() {
+            return this.isStatic;
+        }
+
+        public Object leftShift(Object arg) {
+            registerIfClosure(arg, false);
+            return this;
+        }
+
+        private void registerIfClosure(Object arg, boolean replace) {
+            if (arg instanceof Closure) {
+                Closure callable = (Closure) arg;
+                final List<MetaMethod> list = ClosureMetaMethod.createMethodList(propertyName, theClass, callable);
+                if (list.isEmpty() && this.isStatic) {
+                    Class[] paramTypes = callable.getParameterTypes();
+                    registerStatic(callable, replace, paramTypes);
+                    return;
+                }
+                for (MetaMethod method : list) {
+                    Class[] paramTypes = method.getNativeParameterTypes();
+                    if (this.isStatic) {
+                        registerStatic(callable, replace, paramTypes);
+                    } else {
+                        registerInstance(method, replace, paramTypes);
+                    }
+                }
+            }
+        }
+
+        private void registerStatic(Closure callable, boolean replace, Class[] paramTypes) {
+            Method foundMethod = checkIfMethodExists(theClass, propertyName, paramTypes, true);
+            if (foundMethod != null && !replace)
+                throw new GroovyRuntimeException("Cannot add new static method [" + propertyName + "] for arguments [" + DefaultGroovyMethods.inspect(paramTypes) + "]. It already exists!");
+            registerStaticMethod(propertyName, callable, paramTypes);
+        }
+
+        private void registerInstance(MetaMethod method, boolean replace, Class[] paramTypes) {
+            Method foundMethod = checkIfMethodExists(theClass, propertyName, paramTypes, false);
+            if (foundMethod != null && !replace)
+                throw new GroovyRuntimeException("Cannot add new method [" + propertyName + "] for arguments [" + DefaultGroovyMethods.inspect(paramTypes) + "]. It already exists!");
+            registerInstanceMethod(method);
+        }
+
+        private Method checkIfMethodExists(Class methodClass, String methodName, Class[] paramTypes, boolean staticMethod) {
+            Method foundMethod = null;
+            Method[] methods = methodClass.getMethods();
+            for (Method method : methods) {
+                if (method.getName().equals(methodName) && Modifier.isStatic(method.getModifiers()) == staticMethod) {
+                    if (MetaClassHelper.parametersAreCompatible(paramTypes, method.getParameterTypes())) {
+                        foundMethod = method;
+                        break;
+                    }
+                }
+            }
+            return foundMethod;
+        }
+
+        /* (non-Javadoc)
+           * @see groovy.lang.GroovyObjectSupport#getProperty(java.lang.String)
+           */
+
+        public Object getProperty(String property) {
+            this.propertyName = property;
+            return this;
+        }
+        /* (non-Javadoc)
+           * @see groovy.lang.GroovyObjectSupport#setProperty(java.lang.String, java.lang.Object)
+           */
+
+        public void setProperty(String property, Object newValue) {
+            this.propertyName = property;
+            registerIfClosure(newValue, true);
+        }
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.MetaClassImpl#invokeConstructor(java.lang.Object[])
+      */
+
+    public Object invokeConstructor(Object[] arguments) {
+
+        // TODO This is the only area where this MetaClass needs to do some interception because Groovy's current
+        // MetaClass uses hard coded references to the java.lang.reflect.Constructor class so you can't simply
+        // inject Constructor like you can do properties, methods and fields. When Groovy's MetaClassImpl is
+        // refactored we can fix this
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        MetaMethod method = pickMethod(GROOVY_CONSTRUCTOR, argClasses);
+        if (method != null && method.getParameterTypes().length == arguments.length) {
+            return method.invoke(theClass, arguments);
+        }
+        return super.invokeConstructor(arguments);
+    }
+
+    /**
+     * Handles the ability to use the left shift operator to append new constructors
+     *
+     * @author Graeme Rocher
+     */
+    protected class ExpandoMetaConstructor extends GroovyObjectSupport {
+        public Object leftShift(Closure c) {
+            if (c != null) {
+                final List<MetaMethod> list = ClosureMetaMethod.createMethodList(GROOVY_CONSTRUCTOR, theClass, c);
+                for (MetaMethod method : list) {
+                    Class[] paramTypes = method.getNativeParameterTypes();
+                    Constructor ctor = retrieveConstructor(paramTypes);
+                    if (ctor != null)
+                        throw new GroovyRuntimeException("Cannot add new constructor for arguments [" + DefaultGroovyMethods.inspect(paramTypes) + "]. It already exists!");
+
+                    registerInstanceMethod(method);
+                }
+            }
+
+            return this;
+        }
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.GroovyObject#getMetaClass()
+      */
+
+    public MetaClass getMetaClass() {
+        return myMetaClass;
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+      */
+
+    public Object getProperty(String property) {
+        if (isValidExpandoProperty(property)) {
+            if (property.equals(STATIC_QUALIFIER)) {
+                return new ExpandoMetaProperty(property, true);
+            } else if (property.equals(CONSTRUCTOR)) {
+                return new ExpandoMetaConstructor();
+            } else {
+                if (myMetaClass.hasProperty(this, property) == null)
+                    return new ExpandoMetaProperty(property);
+                else
+                    return myMetaClass.getProperty(this, property);
+            }
+        } else {
+            return myMetaClass.getProperty(this, property);
+        }
+    }
+
+    public static boolean isValidExpandoProperty(String property) {
+        return !(property.equals(META_CLASS) || property.equals(CLASS) || property.equals(META_METHODS) || property.equals(METHODS) || property.equals(PROPERTIES));
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+      */
+
+    public Object invokeMethod(String name, Object args) {
+        final Object[] argsArr = args instanceof Object[] ? (Object[]) args : new Object[]{args};
+        MetaMethod metaMethod = myMetaClass.getMetaMethod(name, argsArr);
+        if (metaMethod != null) {
+            // we have to use doMethodInvoke here instead of simply invoke,
+            // because getMetaMethod may provide a method that can not be called
+            // without further argument transformation, which is done only in 
+            // doMethodInvoke
+            return metaMethod.doMethodInvoke(this, argsArr);
+        }
+
+        if (argsArr.length == 2 && argsArr[0] instanceof Class && argsArr[1] instanceof Closure) {
+            if (argsArr[0] == theClass)
+                registerInstanceMethod(name, (Closure) argsArr[1]);
+            else {
+                registerSubclassInstanceMethod(name, (Class) argsArr[0], (Closure) argsArr[1]);
+            }
+            return null;
+        }
+
+        if (argsArr.length == 1 && argsArr[0] instanceof Closure) {
+            registerInstanceMethod(name, (Closure) argsArr[0]);
+            return null;
+        }
+
+        throw new MissingMethodException(name, getClass(), argsArr);
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.GroovyObject#setMetaClass(groovy.lang.MetaClass)
+      */
+
+    public void setMetaClass(MetaClass metaClass) {
+        this.myMetaClass = metaClass;
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+      */
+
+    public void setProperty(String property, Object newValue) {
+        if (newValue instanceof Closure) {
+            if (property.equals(CONSTRUCTOR)) {
+                property = GROOVY_CONSTRUCTOR;
+            }
+            Closure callable = (Closure) newValue;
+            final List<MetaMethod> list = ClosureMetaMethod.createMethodList(property, theClass, callable);
+            for (MetaMethod method : list) {
+                // here we don't care if the method exists or not we assume the
+                // developer is responsible and wants to override methods where necessary
+                registerInstanceMethod(method);
+            }
+        } else {
+            registerBeanProperty(property, newValue);
+        }
+    }
+
+    public ExpandoMetaClass define(Closure closure) {
+        final DefiningClosure definer = new DefiningClosure();
+        Object delegate = closure.getDelegate();
+        closure.setDelegate(definer);
+        closure.setResolveStrategy(Closure.DELEGATE_ONLY);
+        closure.call((Object)null);
+        closure.setDelegate(delegate);
+        closure.setResolveStrategy(Closure.DELEGATE_FIRST);
+        definer.definition = false;
+        return this;
+    }
+
+    protected synchronized void performOperationOnMetaClass(Callable c) {
+        try {
+            writeLock.lock();
+            if (allowChangesAfterInit) {
+                setInitialized(false);
+            }
+            c.call();
+        }
+        finally {
+            if (initCalled) {
+                setInitialized(true);
+            }
+            // downgrade to readlock before releasing just in case
+            readLock.lock();
+            writeLock.unlock();
+            readLock.unlock();
+        }
+    }
+    
+    protected void checkInitalised() {
+        try {
+            readLock.lock();
+            super.checkInitalised();
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Registers a new bean property
+     *
+     * @param property The property name
+     * @param newValue The properties initial value
+     */
+    public void registerBeanProperty(final String property, final Object newValue) {
+        performOperationOnMetaClass(new Callable() {
+            public void call() {
+                Class type = newValue == null ? Object.class : newValue.getClass();
+
+                MetaBeanProperty mbp = newValue instanceof MetaBeanProperty ? (MetaBeanProperty) newValue : new ThreadManagedMetaBeanProperty(theClass, property, type, newValue);
+
+                final MetaMethod getter = mbp.getGetter();
+                final MethodKey getterKey = new DefaultCachedMethodKey(theClass, getter.getName(), CachedClass.EMPTY_ARRAY, false);
+                final MetaMethod setter = mbp.getSetter();
+                final MethodKey setterKey = new DefaultCachedMethodKey(theClass, setter.getName(), setter.getParameterTypes(), false);
+                addMetaMethod(getter);
+                addMetaMethod(setter);
+
+                expandoMethods.put(setterKey, setter);
+                expandoMethods.put(getterKey, getter);
+                expandoProperties.put(mbp.getName(), mbp);
+
+                addMetaBeanProperty(mbp);
+                performRegistryCallbacks();
+            }
+
+        });
+    }
+
+    /**
+     * Registers a new instance method for the given method name and closure on this MetaClass
+     *
+     * @param metaMethod
+     */
+    public void registerInstanceMethod(final MetaMethod metaMethod) {
+        final boolean inited = this.initCalled;
+        performOperationOnMetaClass(new Callable() {
+            public void call() {
+                String methodName = metaMethod.getName();
+                checkIfGroovyObjectMethod(metaMethod);
+                MethodKey key = new DefaultCachedMethodKey(theClass, methodName, metaMethod.getParameterTypes(), false);
+
+                if (isInitialized()) {
+                    throw new RuntimeException("Already initialized, cannot add new method: " + metaMethod);
+                }
+                // we always adds meta methods to class itself
+                addMetaMethodToIndex(metaMethod, metaMethodIndex.getHeader(theClass));
+
+                dropMethodCache(methodName);
+                expandoMethods.put(key, metaMethod);
+
+                if (inited && isGetter(methodName, metaMethod.getParameterTypes())) {
+                    String propertyName = getPropertyForGetter(methodName);
+                    registerBeanPropertyForMethod(metaMethod, propertyName, true, false);
+
+                } else if (inited && isSetter(methodName, metaMethod.getParameterTypes())) {
+                    String propertyName = getPropertyForSetter(methodName);
+                    registerBeanPropertyForMethod(metaMethod, propertyName, false, false);
+                }
+                performRegistryCallbacks();
+            }
+
+        });
+    }
+
+    public void registerInstanceMethod(String name, Closure closure) {
+        final List<MetaMethod> list = ClosureMetaMethod.createMethodList(name, theClass, closure);
+        for (MetaMethod method : list) {
+            registerInstanceMethod(method);
+        }
+    }
+
+    /**
+     * Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
+     *
+     * @return A list of MetaMethods
+     * @see MetaObjectProtocol#getMethods()
+     */
+    public List<MetaMethod> getMethods() {
+        List<MetaMethod> methodList = new ArrayList<MetaMethod>();
+        methodList.addAll(this.expandoMethods.values());
+        methodList.addAll(super.getMethods());
+        return methodList;
+    }
+
+    public List<MetaProperty> getProperties() {
+        List<MetaProperty> propertyList = new ArrayList<MetaProperty>();
+        propertyList.addAll(super.getProperties());
+        return propertyList;
+    }
+
+
+    private void performRegistryCallbacks() {
+        MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
+        incVersion();
+        if (!modified) {
+            modified = true;
+            // Implementation note: By default Groovy uses soft references to store MetaClass
+            // this insures the registry doesn't grow and get out of hand. By doing this we're
+            // saying this this EMC will be a hard reference in the registry. As we're only
+            // going have a small number of classes that have modified EMC this is ok
+            if (inRegistry) {
+                MetaClass currMetaClass = registry.getMetaClass(theClass);
+                if (!(currMetaClass instanceof ExpandoMetaClass) && currMetaClass instanceof AdaptingMetaClass) {
+                    ((AdaptingMetaClass) currMetaClass).setAdaptee(this);
+                } else {
+                    registry.setMetaClass(theClass, this);
+                }
+            }
+
+        }
+    }
+
+    private void registerBeanPropertyForMethod(MetaMethod metaMethod, String propertyName, boolean getter, boolean isStatic) {
+        Map<String, MetaProperty> propertyCache = isStatic ? staticBeanPropertyCache : beanPropertyCache;
+        MetaBeanProperty beanProperty = (MetaBeanProperty) propertyCache.get(propertyName);
+        if (beanProperty==null) {
+            MetaProperty metaProperty = super.getMetaProperty(propertyName);
+            if (metaProperty instanceof MetaBeanProperty) {
+                boolean staticProp = Modifier.isStatic(metaProperty.getModifiers());
+                if (isStatic==staticProp) {
+                    beanProperty = (MetaBeanProperty) metaProperty;
+                }
+            }
+        }
+        if (beanProperty == null) {
+            if (getter)
+                beanProperty = new MetaBeanProperty(propertyName, Object.class, metaMethod, null);
+            else
+                beanProperty = new MetaBeanProperty(propertyName, Object.class, null, metaMethod);
+
+            propertyCache.put(propertyName, beanProperty);
+        } else {
+            if (getter) {
+                MetaMethod setterMethod = beanProperty.getSetter();
+                Class type = setterMethod != null ? setterMethod.getParameterTypes()[0].getTheClass() : Object.class;
+                beanProperty = new MetaBeanProperty(propertyName, type, metaMethod, setterMethod);
+                propertyCache.put(propertyName, beanProperty);
+            } else {
+                MetaMethod getterMethod = beanProperty.getGetter();
+                beanProperty = new MetaBeanProperty(propertyName, metaMethod.getParameterTypes()[0].getTheClass(), getterMethod, metaMethod);
+                propertyCache.put(propertyName, beanProperty);
+            }
+        }
+        expandoProperties.put(beanProperty.getName(), beanProperty);
+        addMetaBeanProperty(beanProperty);
+    }
+
+    protected void registerStaticMethod(final String name, final Closure callable) {
+        registerStaticMethod(name, callable, null);
+    }
+
+    /**
+     * Registers a new static method for the given method name and closure on this MetaClass
+     *
+     * @param name     The method name
+     * @param callable The callable Closure
+     */
+    protected void registerStaticMethod(final String name, final Closure callable, final Class[] paramTypes) {
+        performOperationOnMetaClass(new Callable() {
+            public void call() {
+                String methodName;
+                if (name.equals(METHOD_MISSING))
+                    methodName = STATIC_METHOD_MISSING;
+                else if (name.equals(PROPERTY_MISSING))
+                    methodName = STATIC_PROPERTY_MISSING;
+                else
+                    methodName = name;
+
+                ClosureStaticMetaMethod metaMethod = null;
+
+                if (paramTypes != null) {
+                    metaMethod = new ClosureStaticMetaMethod(methodName, theClass, callable, paramTypes);
+                } else {
+                    metaMethod = new ClosureStaticMetaMethod(methodName, theClass, callable);
+                }
+
+                if (methodName.equals(INVOKE_METHOD_METHOD) && callable.getParameterTypes().length == 2) {
+                    invokeStaticMethodMethod = metaMethod;
+                } else {
+                    if (methodName.equals(METHOD_MISSING)) {
+                        methodName = STATIC_METHOD_MISSING;
+                    }
+                    MethodKey key = new DefaultCachedMethodKey(theClass, methodName, metaMethod.getParameterTypes(), false);
+
+                    addMetaMethod(metaMethod);
+                    dropStaticMethodCache(methodName);
+//                    cacheStaticMethod(key,metaMethod);
+
+                    if (isGetter(methodName, metaMethod.getParameterTypes())) {
+                        String propertyName = getPropertyForGetter(methodName);
+                        registerBeanPropertyForMethod(metaMethod, propertyName, true, true);
+
+                    } else if (isSetter(methodName, metaMethod.getParameterTypes())) {
+                        String propertyName = getPropertyForSetter(methodName);
+                        registerBeanPropertyForMethod(metaMethod, propertyName, false, true);
+                    }
+                    performRegistryCallbacks();
+                    expandoMethods.put(key, metaMethod);
+                }
+            }
+
+        });
+    }
+
+    protected Object getSubclassMetaMethods(String methodName) {
+        if (!isModified())
+            return null;
+
+        return expandoSubclassMethods.get(methodName);
+    }
+
+    /**
+     * @return The Java class enhanced by this MetaClass
+     */
+    public Class getJavaClass() {
+        return theClass;
+    }
+
+    /**
+     * Called from ExpandoMetaClassCreationHandle in the registry if it exists to
+     * set up inheritance handling
+     *
+     * @param modifiedSuperExpandos A list of modified super ExpandoMetaClass
+     */
+    public void refreshInheritedMethods(Set modifiedSuperExpandos) {
+        for (Iterator i = modifiedSuperExpandos.iterator(); i.hasNext();) {
+            ExpandoMetaClass superExpando = (ExpandoMetaClass) i.next();
+            if (superExpando != this) {
+                refreshInheritedMethods(superExpando);
+            }
+        }
+    }
+
+    private void refreshInheritedMethods(ExpandoMetaClass superExpando) {
+        List<MetaMethod> metaMethods = superExpando.getExpandoMethods();
+        for (MetaMethod metaMethod : metaMethods) {
+            if (metaMethod.isStatic()) {
+                if (superExpando.getTheClass() != getTheClass())
+                    continue; // don't inherit static methods except our own
+                registerStaticMethod(metaMethod.getName(), (Closure) ((ClosureStaticMetaMethod) metaMethod).getClosure().clone());
+            } else
+                addSuperMethodIfNotOverridden(metaMethod);
+        }
+        Collection<MetaProperty> metaProperties = superExpando.getExpandoProperties();
+        for (Object metaProperty : metaProperties) {
+            MetaBeanProperty property = (MetaBeanProperty) metaProperty;
+            expandoProperties.put(property.getName(), property);
+            addMetaBeanProperty(property);
+        }
+    }
+
+
+    /**
+     * Returns a list of expando MetaMethod instances added to this ExpandoMetaClass
+     *
+     * @return the expandoMethods
+     */
+    public List<MetaMethod> getExpandoMethods() {
+        return Collections.unmodifiableList(DefaultGroovyMethods.toList(expandoMethods.values()));
+    }
+
+
+    /**
+     * Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
+     *
+     * @return the expandoProperties
+     */
+    public Collection<MetaProperty> getExpandoProperties() {
+        return Collections.unmodifiableCollection(expandoProperties.values());
+    }
+
+    /**
+     * Overrides default implementation just in case invokeMethod has been overridden by ExpandoMetaClass
+     *
+     * @see groovy.lang.MetaClassImpl#invokeMethod(Class, Object, String, Object[], boolean, boolean)
+     */
+    public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+        if (invokeMethodMethod != null) {
+            MetaClassHelper.unwrap(originalArguments);
+            return invokeMethodMethod.invoke(object, new Object[]{methodName, originalArguments});
+        }
+        return super.invokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass);
+    }
+
+    /**
+     * Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
+     *
+     * @see MetaClassImpl#invokeStaticMethod(Object, String, Object[])
+     */
+    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
+        if (invokeStaticMethodMethod != null) {
+            MetaClassHelper.unwrap(arguments);
+            return invokeStaticMethodMethod.invoke(object, new Object[]{methodName, arguments});
+        }
+        return super.invokeStaticMethod(object, methodName, arguments);
+    }
+
+    /**
+     * Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#getProperty(Class, Object, String, boolean, boolean)
+     */
+    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
+        if (hasOverrideGetProperty(name) && getJavaClass().isInstance(object)) {
+            return getPropertyMethod.invoke(object, new Object[]{name});
+        }
+
+        if ("mixedIn".equals(name)) {
+            return new MixedInAccessor(object, mixinClasses);
+        }
+
+        return super.getProperty(sender, object, name, useSuper, fromInsideClass);
+    }
+
+    /**
+     * Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#getProperty(Object, String)
+     */
+    public Object getProperty(Object object, String name) {
+        if (hasOverrideGetProperty(name) && getJavaClass().isInstance(object)) {
+            return getPropertyMethod.invoke(object, new Object[]{name});
+        }
+        return super.getProperty(object, name);
+    }
+
+    private boolean hasOverrideGetProperty(String name) {
+        return getPropertyMethod != null && !name.equals(META_CLASS_PROPERTY) && !name.equals(CLASS_PROPERTY);
+    }
+
+    /**
+     * Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#setProperty(Class, Object, String, Object, boolean, boolean)
+     */
+
+    public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
+        if (setPropertyMethod != null && !name.equals(META_CLASS_PROPERTY) && getJavaClass().isInstance(object)) {
+            setPropertyMethod.invoke(object, new Object[]{name, newValue});
+            return;
+        }
+        super.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
+    }
+
+    /**
+     * Looks up an existing MetaProperty by name
+     *
+     * @param name The name of the MetaProperty
+     * @return The MetaProperty or null if it doesn't exist
+     */
+    public MetaProperty getMetaProperty(String name) {
+        MetaProperty mp = this.expandoProperties.get(name);
+        if (mp != null) return mp;
+        return super.getMetaProperty(name);
+    }
+
+    /**
+     * Returns true if the MetaClass has the given property
+     *
+     * @param name The name of the MetaProperty
+     * @return True it exists as a MetaProperty
+     */
+    public boolean hasMetaProperty(String name) {
+        return getMetaProperty(name) != null;
+    }
+
+    /**
+     * Checks whether a MetaMethod for the given name and arguments exists
+     *
+     * @param name The name of the MetaMethod
+     * @param args The arguments to the meta method
+     * @return True if the method exists otherwise null
+     */
+    public boolean hasMetaMethod(String name, Class[] args) {
+        return super.pickMethod(name, args) != null;
+    }
+
+    /**
+     * Determine if this method name suffix is a legitimate bean property name.
+     * Either the first or second letter must be upperCase for that to be true.
+     */
+    private static boolean isPropertyName(String name) {
+        return ((name.length() > 0) && Character.isUpperCase(name.charAt(0))) || ((name.length() > 1) && Character.isUpperCase(name.charAt(1)));
+    }
+
+    /**
+     * Returns true if the name of the method specified and the number of arguments make it a javabean property
+     *
+     * @param name True if its a Javabean property
+     * @param args The arguments
+     * @return True if it is a javabean property method
+     */
+    private boolean isGetter(String name, CachedClass[] args) {
+        if (name == null || name.length() == 0 || args == null) return false;
+        if (args.length != 0) return false;
+
+        if (name.startsWith("get")) {
+            name = name.substring(3);
+            return isPropertyName(name);
+        } else if (name.startsWith("is")) {
+            name = name.substring(2);
+            return isPropertyName(name);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a property name equivalent for the given getter name or null if it is not a getter
+     *
+     * @param getterName The getter name
+     * @return The property name equivalent
+     */
+    private String getPropertyForGetter(String getterName) {
+        if (getterName == null || getterName.length() == 0) return null;
+
+        if (getterName.startsWith("get")) {
+            String prop = getterName.substring(3);
+            return MetaClassHelper.convertPropertyName(prop);
+        } else if (getterName.startsWith("is")) {
+            String prop = getterName.substring(2);
+            return MetaClassHelper.convertPropertyName(prop);
+        }
+        return null;
+    }
+
+    /**
+     * Returns a property name equivalent for the given setter name or null if it is not a getter
+     *
+     * @param setterName The setter name
+     * @return The property name equivalent
+     */
+    public String getPropertyForSetter(String setterName) {
+        if (setterName == null || setterName.length() == 0) return null;
+
+        if (setterName.startsWith("set")) {
+            String prop = setterName.substring(3);
+            return MetaClassHelper.convertPropertyName(prop);
+        }
+        return null;
+    }
+
+    public boolean isSetter(String name, CachedClass[] args) {
+        if (name == null || name.length() == 0 || args == null) return false;
+
+        if (name.startsWith("set")) {
+            if (args.length != 1) return false;
+            name = name.substring(3);
+            return isPropertyName(name);
+        }
+
+        return false;
+    }
+
+    public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args) {
+        if (invokeMethodMethod != null)
+            return new PojoMetaClassSite(site, this);
+
+        return super.createPojoCallSite(site, receiver, args);
+    }
+
+    public CallSite createStaticSite(CallSite site, Object[] args) {
+        if (invokeStaticMethodMethod != null)
+            return new StaticMetaClassSite(site, this);
+
+        return super.createStaticSite(site, args);
+    }
+
+    public boolean hasCustomStaticInvokeMethod() {return invokeStaticMethodMethod!=null; }
+
+    public CallSite createPogoCallSite(CallSite site, Object[] args) {
+        if (invokeMethodMethod != null)
+            return new PogoMetaClassSite(site, this);
+        return super.createPogoCallSite(site, args);
+    }
+
+    public CallSite createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args) {
+        if (invokeMethodMethod != null)
+            return new PogoMetaClassSite(site, this);
+        return super.createPogoCallCurrentSite(site, sender, args);
+    }
+    
+    @Override
+    public MetaMethod retrieveConstructor(Object[] args) {
+        Class[] params = MetaClassHelper.convertToTypeArray(args);
+        MetaMethod method = pickMethod(GROOVY_CONSTRUCTOR, params);
+        if (method!=null) return method;
+        return super.retrieveConstructor(args);
+    }
+
+    public CallSite createConstructorSite(CallSite site, Object[] args) {
+        Class[] params = MetaClassHelper.convertToTypeArray(args);
+        MetaMethod method = pickMethod(GROOVY_CONSTRUCTOR, params);
+        if (method != null && method.getParameterTypes().length == args.length) {
+            if (method.getDeclaringClass().getTheClass().equals(getTheClass())) {
+                return new ConstructorMetaMethodSite(site, this, method, params);
+            }
+        }
+
+        return super.createConstructorSite(site, args);
+    }
+
+    private class SubClassDefiningClosure extends GroovyObjectSupport {
+        private final Class klazz;
+
+        public SubClassDefiningClosure(Class klazz) {
+            this.klazz = klazz;
+        }
+
+        public Object invokeMethod(String name, Object obj) {
+            if (obj instanceof Object[]) {
+                Object args[] = (Object[]) obj;
+                if (args.length == 1 && args[0] instanceof Closure) {
+                    registerSubclassInstanceMethod(name, klazz, (Closure) args[0]);
+                    return null;
+                }
+            }
+
+            throw new MissingMethodException(name, getClass(), new Object[]{obj});
+        }
+    }
+
+    private class DefiningClosure extends GroovyObjectSupport {
+        boolean definition = true;
+
+        public void mixin(Class category) {
+            mixin(Collections.singletonList(category));
+        }
+
+        public void mixin(List categories) {
+            DefaultGroovyMethods.mixin(ExpandoMetaClass.this, categories);
+        }
+
+        public void mixin(Class[] categories) {
+            DefaultGroovyMethods.mixin(ExpandoMetaClass.this, categories);
+        }
+
+        public void define(Class subClass, Closure closure) {
+            final SubClassDefiningClosure definer = new SubClassDefiningClosure(subClass);
+            closure.setDelegate(definer);
+            closure.setResolveStrategy(Closure.DELEGATE_FIRST);
+            closure.call((Object)null);
+        }
+
+        public Object invokeMethod(String name, Object obj) {
+            try {
+                return getMetaClass().invokeMethod(this, name, obj);
+            }
+            catch (MissingMethodException mme) {
+                if (obj instanceof Object[]) {
+                    if (STATIC_QUALIFIER.equals(name)) {
+                        final StaticDefiningClosure staticDef = new StaticDefiningClosure();
+                        Closure c = (Closure) ((Object[]) obj)[0];
+                        c.setDelegate(staticDef);
+                        c.setResolveStrategy(Closure.DELEGATE_ONLY);
+                        c.call((Object)null);
+                        return null;
+                    }
+                    Object args[] = (Object[]) obj;
+                    if (args.length == 1 && args[0] instanceof Closure) {
+                        registerInstanceMethod(name, (Closure) args[0]);
+                    } else if (args.length == 2 && args[0] instanceof Class && args[1] instanceof Closure)
+                        registerSubclassInstanceMethod(name, (Class) args[0], (Closure) args[1]);
+                    else
+                        ExpandoMetaClass.this.setProperty(name, ((Object[]) obj)[0]);
+
+                    return null;
+                }
+
+                throw mme;
+            }
+        }
+
+        public void setProperty(String property, Object newValue) {
+            ExpandoMetaClass.this.setProperty(property, newValue);
+        }
+
+        public Object getProperty(String property) {
+            if (STATIC_QUALIFIER.equals(property))
+                return new StaticDefiningClosure();
+
+            if (definition)
+                return new ExpandoMetaProperty(property);
+            else
+                throw new MissingPropertyException(property, getClass());
+        }
+    }
+
+    private class StaticDefiningClosure extends ExpandoMetaProperty {
+        protected StaticDefiningClosure() {
+            super(STATIC_QUALIFIER, true);
+        }
+
+        public Object invokeMethod(String name, Object obj) {
+            if (obj instanceof Object[]) {
+                final Object[] args = (Object[]) obj;
+                if (args.length == 1 && args[0] instanceof Closure) {
+                    registerStaticMethod(name, (Closure) args[0]);
+                    return null;
+                }
+            }
+
+            throw new MissingMethodException(name, getClass(), obj instanceof Object[] ? (Object[]) obj : new Object[]{obj});
+        }
+    }
+
+    private static class MixedInAccessor {
+        private final Object object;
+        private final Set<MixinInMetaClass> mixinClasses;
+
+        public MixedInAccessor(Object object, Set<MixinInMetaClass> mixinClasses) {
+            this.object = object;
+            this.mixinClasses = mixinClasses;
+        }
+
+        public Object getAt(Class key) {
+            if (key.isAssignableFrom(object.getClass())) {
+                return new GroovyObjectSupport() {
+                    {
+                        final MetaClass ownMetaClass = InvokerHelper.getMetaClass(object.getClass());
+                        setMetaClass(new OwnedMetaClass(ownMetaClass) {
+                            protected Object getOwner() {
+                                return object;
+                            }
+
+                            protected MetaClass getOwnerMetaClass(Object owner) {
+                                return getAdaptee();
+                            }
+                        });
+                    }
+                };
+            }
+
+            for (final MixinInMetaClass mixin : mixinClasses) {
+                if (key.isAssignableFrom(mixin.getMixinClass().getTheClass())) {
+                    return new GroovyObjectSupport() {
+                        {
+                            final Object mixedInInstance = mixin.getMixinInstance(object);
+                            setMetaClass(new OwnedMetaClass(InvokerHelper.getMetaClass(mixedInInstance)) {
+                                @Override
+                                protected Object getOwner() {
+                                    return mixedInInstance;
+                                }
+
+                                @Override
+                                protected MetaClass getOwnerMetaClass(Object owner) {
+                                    return ((MixedInMetaClass) getAdaptee()).getAdaptee();
+                                }
+                            });
+                        }
+                    };
+                }
+            }
+
+            throw new RuntimeException("Class " + key + " isn't mixed in " + object.getClass());
+        }
+
+        public void putAt(Class key, Object value) {
+            for (MixinInMetaClass mixin : mixinClasses)
+                if (mixin.getMixinClass().getTheClass() == key) {
+                    mixin.setMixinInstance(object, value);
+                    return;
+                }
+
+            throw new RuntimeException("Class " + key + " isn't mixed in " + object.getClass());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ExpandoMetaClassCreationHandle.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ExpandoMetaClassCreationHandle.java b/src/main/groovy/groovy/lang/ExpandoMetaClassCreationHandle.java
new file mode 100644
index 0000000..6e411a5
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ExpandoMetaClassCreationHandle.java
@@ -0,0 +1,95 @@
+/*
+ *  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 groovy.lang.MetaClassRegistry.MetaClassCreationHandle;
+import org.codehaus.groovy.reflection.ClassInfo;
+
+/**
+ * <p>A handle for the MetaClassRegistry that changes all classes loaded into the Grails VM
+ * to use ExpandoMetaClass instances
+ *
+ * <p>The handle should be registered with the Groovy runtime <strong>before</strong> Groovy loads, for example
+ * in your main method.
+ *
+ * <code>GroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle()</code>
+ *
+ * @see groovy.lang.MetaClassRegistry
+ * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle
+ * @see org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl#setMetaClassCreationHandle(groovy.lang.MetaClassRegistry.MetaClassCreationHandle)
+ *
+ * @author Graeme Rocher
+ * @since 1.5
+ */
+public class ExpandoMetaClassCreationHandle extends MetaClassCreationHandle {
+
+    public static final ExpandoMetaClassCreationHandle instance = new ExpandoMetaClassCreationHandle();
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle#create(java.lang.Class, groovy.lang.MetaClassRegistry)
+     */
+    protected MetaClass createNormalMetaClass(Class theClass, MetaClassRegistry registry) {
+        if(theClass != ExpandoMetaClass.class) {
+            return new ExpandoMetaClass(theClass, true, true);
+        }
+        else {
+            return super.createNormalMetaClass(theClass, registry);
+        }
+    }
+
+    /**
+     * Registers a modified ExpandoMetaClass with the creation handle
+     *
+     * @param emc The EMC
+     */
+    public void registerModifiedMetaClass(ExpandoMetaClass emc) {
+        final Class klazz = emc.getJavaClass();
+        GroovySystem.getMetaClassRegistry().setMetaClass(klazz,emc);
+    }
+
+    public boolean hasModifiedMetaClass(ExpandoMetaClass emc) {
+        return emc.getClassInfo().getModifiedExpando() != null;
+    }
+
+    /**
+     * <p>Enables the ExpandoMetaClassCreationHandle with the registry
+     *
+     * <code>ExpandoMetaClassCreationHandle.enable();</code>
+     *
+     */
+    public static void enable() {
+        final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+        synchronized (metaClassRegistry) {
+            if (metaClassRegistry.getMetaClassCreationHandler() != instance) {
+                ClassInfo.clearModifiedExpandos();
+                metaClassRegistry.setMetaClassCreationHandle(instance);
+            }
+        }
+    }
+
+    public static void disable() {
+        final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+        synchronized (metaClassRegistry) {
+            if (metaClassRegistry.getMetaClassCreationHandler() == instance) {
+                ClassInfo.clearModifiedExpandos();
+                metaClassRegistry.setMetaClassCreationHandle(new MetaClassCreationHandle());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GString.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GString.java b/src/main/groovy/groovy/lang/GString.java
new file mode 100644
index 0000000..8323bf2
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GString.java
@@ -0,0 +1,245 @@
+/*
+ *  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.codehaus.groovy.runtime.GStringImpl;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.StringGroovyMethods;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Represents a String which contains embedded values such as "hello there
+ * ${user} how are you?" which can be evaluated lazily. Advanced users can
+ * iterate over the text and values to perform special processing, such as for
+ * performing SQL operations, the values can be substituted for ? and the
+ * actual value objects can be bound to a JDBC statement. The lovely name of
+ * this class was suggested by Jules Gosnell and was such a good idea, I
+ * couldn't resist :)
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable, Buildable, Serializable {
+
+    static final long serialVersionUID = -2638020355892246323L;
+
+    /**
+     * A GString containing a single empty String and no values.
+     */
+    public static final GString EMPTY = new GString(new Object[0]) {
+        @Override
+        public String[] getStrings() {
+            return new String[]{""};
+        }
+    };
+    public static final String[] EMPTY_STRING_ARRAY = new String[0];
+    public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+    private final Object[] values;
+
+    public GString(Object values) {
+        this.values = (Object[]) values;
+    }
+
+    public GString(Object[] values) {
+        this.values = values;
+    }
+
+    // will be static in an instance
+
+    public abstract String[] getStrings();
+
+    /**
+     * Overloaded to implement duck typing for Strings
+     * so that any method that can't be evaluated on this
+     * object will be forwarded to the toString() object instead.
+     */
+    @Override
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        catch (MissingMethodException e) {
+            // lets try invoke the method on the real String
+            return InvokerHelper.invokeMethod(toString(), name, args);
+        }
+    }
+
+    public Object[] getValues() {
+        return values;
+    }
+
+    public GString plus(GString that) {
+        List<String> stringList = new ArrayList<String>(Arrays.asList(getStrings()));
+        List<Object> valueList = new ArrayList<Object>(Arrays.asList(getValues()));
+
+        List<String> thatStrings = Arrays.asList(that.getStrings());
+
+        int stringListSize = stringList.size();
+        if (stringListSize > valueList.size()) {
+            thatStrings = new ArrayList<String>(thatStrings);
+            // merge onto end of previous GString to avoid an empty bridging value
+            int lastIndexOfStringList = stringListSize - 1;
+            String s = stringList.get(lastIndexOfStringList);
+            s += thatStrings.remove(0);
+            stringList.set(lastIndexOfStringList, s);
+        }
+
+        stringList.addAll(thatStrings);
+        valueList.addAll(Arrays.asList(that.getValues()));
+
+        final String[] newStrings = stringList.toArray(EMPTY_STRING_ARRAY);
+        final Object[] newValues = valueList.toArray();
+
+        return new GStringImpl(newValues, newStrings);
+    }
+
+    public GString plus(String that) {
+        return plus(new GStringImpl(EMPTY_OBJECT_ARRAY, new String[] { that }));
+    }
+
+    public int getValueCount() {
+        return values.length;
+    }
+
+    public Object getValue(int idx) {
+        return values[idx];
+    }
+
+    @Override
+    public String toString() {
+        StringWriter buffer = new StringWriter();
+        try {
+            writeTo(buffer);
+        }
+        catch (IOException e) {
+            throw new StringWriterIOException(e);
+        }
+        return buffer.toString();
+    }
+
+    @Override
+    public Writer writeTo(Writer out) throws IOException {
+        String[] s = getStrings();
+        int numberOfValues = values.length;
+        for (int i = 0, size = s.length; i < size; i++) {
+            out.write(s[i]);
+            if (i < numberOfValues) {
+                final Object value = values[i];
+
+                if (value instanceof Closure) {
+                    final Closure c = (Closure) value;
+
+                    if (c.getMaximumNumberOfParameters() == 0) {
+                        InvokerHelper.write(out, c.call());
+                    } else if (c.getMaximumNumberOfParameters() == 1) {
+                        c.call(out);
+                    } else {
+                        throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
+                                + c.getMaximumNumberOfParameters() + " parameters");
+                    }
+                } else {
+                    InvokerHelper.write(out, value);
+                }
+            }
+        }
+        return out;
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.Buildable#build(groovy.lang.GroovyObject)
+     */
+
+    @Override
+    public void build(final GroovyObject builder) {
+        final String[] s = getStrings();
+        final int numberOfValues = values.length;
+
+        for (int i = 0, size = s.length; i < size; i++) {
+            builder.getProperty("mkp");
+            builder.invokeMethod("yield", new Object[]{s[i]});
+            if (i < numberOfValues) {
+                builder.getProperty("mkp");
+                builder.invokeMethod("yield", new Object[]{values[i]});
+            }
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return 37 + toString().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (that instanceof GString) {
+            return equals((GString) that);
+        }
+        return false;
+    }
+
+    public boolean equals(GString that) {
+        return toString().equals(that.toString());
+    }
+
+    @Override
+    public int compareTo(Object that) {
+        return toString().compareTo(that.toString());
+    }
+
+    @Override
+    public char charAt(int index) {
+        return toString().charAt(index);
+    }
+
+    @Override
+    public int length() {
+        return toString().length();
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        return toString().subSequence(start, end);
+    }
+
+    /**
+     * Turns a String into a regular expression pattern
+     *
+     * @return the regular expression pattern
+     */
+    public Pattern negate() {
+        return StringGroovyMethods.bitwiseNegate(toString());
+    }
+
+    public byte[] getBytes() {
+        return toString().getBytes();
+    }
+
+    public byte[] getBytes(String charset) throws UnsupportedEncodingException {
+       return toString().getBytes(charset);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GeneratedGroovyProxy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GeneratedGroovyProxy.java b/src/main/groovy/groovy/lang/GeneratedGroovyProxy.java
new file mode 100644
index 0000000..55b25b5
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GeneratedGroovyProxy.java
@@ -0,0 +1,34 @@
+/*
+ *  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;
+
+/**
+ * An interface implemented by some Groovy proxies which gives access to the proxied object. In particular, dynamically
+ * generated proxies that implement traits will implement this interface in order to give access to the proxied
+ * instance.
+ *
+ * @author Cedric Champeau
+ * @since 2.3.0
+ */
+public interface GeneratedGroovyProxy {
+    /**
+     * @return the proxy target.
+     */
+    Object getProxyTarget();
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Grab.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Grab.java b/src/main/groovy/groovy/lang/Grab.java
new file mode 100644
index 0000000..89be8b3
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Grab.java
@@ -0,0 +1,143 @@
+/*
+ *  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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to grab the referenced artifact and its dependencies and make it available on the Classpath.
+ * <p>
+ * Some examples:
+ * <pre>
+ * {@code @Grab}(group='commons-lang', module='commons-lang', version='2.4')
+ * import org.apache.commons.lang.WordUtils
+ * println "Hello ${WordUtils.capitalize('world')}"
+ * </pre>
+ * Or using the compact Gradle-inspired syntax:
+ * <pre>
+ * {@code @Grab}('commons-lang:commons-lang:2.4')
+ * import org.apache.commons.lang.WordUtils
+ * println "Hello ${WordUtils.capitalize('world')}"
+ * </pre>
+ * or the same thing again using the Ivy-inspired syntax variant:
+ * <pre>
+ * {@code @Grab}('commons-lang#commons-lang;2.4')
+ * import org.apache.commons.lang.WordUtils
+ * println "Hello ${WordUtils.capitalize('world')}"
+ * </pre>
+ * Further information such as where artifacts are downloaded to, how to add additional resolvers,
+ * how to customise artifact resolution etc., can be found on the Grape documentation page:
+ * <a href="http://groovy-lang.org/grape.html">http://groovy-lang.org/grape.html</a>.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.TYPE})
+public @interface Grab {
+    /**
+     * The organisation or group, e.g.: "org.apache.ant". A non-empty value is required unless value() is used.
+     */
+    String group() default "";
+
+    /**
+     * The module or artifact, e.g.: "ant-junit". A non-empty value is required unless value() is used.
+     */
+    String module() default "";
+
+    /**
+     * The revision or version, e.g.: "1.7.1". A non-empty value is required unless value() is used.
+     */
+    String version() default "";
+
+    /**
+     * The classifier if in use, e.g.: "jdk14"
+     */
+    String classifier() default "";
+
+    /**
+     * Defaults to {@code true} but set to {@code false} if you don't want transitive dependencies also to be downloaded.
+     * You may then need additional {@code @Grab} statements for any required dependencies.
+     */
+    boolean transitive() default true;
+
+    /**
+     * Defaults to {@code false} but set to {@code true} to indicate to the underlying Ivy conflict manager that this
+     * dependency should be forced to the given revision. Otherwise, depending on the conflict manager in play, a later
+     * compatible version might be used instead.
+     */
+    boolean force() default false;
+
+    /**
+     * Defaults to {@code false} but set to {@code true} if the dependency artifacts may change without a corresponding
+     * revision change. Not normally recommended but may be useful for certain kinds of snapshot artifacts.
+     * May reduce the amount of underlying Ivy caching. Proper behavior may be dependent on the resolver in use.
+     */
+    boolean changing() default false;
+
+    /**
+     * The configuration if in use (normally only used by internal ivy repositories).
+     * One or more comma separated values with or without square brackets,
+     * e.g.&#160;for hibernate you might have "default,proxool,oscache" or "[default,dbcp,swarmcache]".
+     * This last hibernate example assumes you have set up such configurations in your local Ivy repo
+     * and have changed your grape config (using grapeConfig.xml) or the {@code @GrabConfig} annotation
+     * to point to that repo.
+     */
+    String conf() default "";
+
+    /**
+     * The extension of the artifact (normally safe to leave at default value of "jar" but other values like "zip"
+     * are sometimes useful).
+     */
+    String ext() default "";
+
+    /**
+     * The type of the artifact (normally safe to leave at default value of "jar" but other values like "sources" and "javadoc" are sometimes useful).
+     * But see also the "classifier" attribute which is also sometimes used for "sources" and "javadoc".
+     */
+    String type() default "";
+
+    /**
+     * Allows a more compact convenience form in one of two formats with optional appended attributes.
+     * Must not be used if group(), module() or version() are used.
+     * <p>
+     * You can choose either format but not mix-n-match:<br>
+     * {@code group:module:version:classifier@ext} (where only group and module are required)<br>
+     * {@code group#module;version[confs]} (where only group and module are required and confs,
+     * if used, is one or more comma separated configuration names)<br>
+     * In addition, you can add any valid Ivy attributes at the end of your string value using
+     * semi-colon separated name = value pairs, e.g.:<br>
+     * {@code @Grab('junit:junit:*;transitive=false')}<br>
+     * {@code @Grab('group=junit;module=junit;version=4.8.2;classifier=javadoc')}<br>
+     */
+    String value() default "";
+
+    /**
+     * By default, when a {@code @Grab} annotation is used, a {@code Grape.grab()} call is added
+     * to the static initializers of the class the annotatable node appears in.
+     * If you wish to disable this, add {@code initClass=false} to the annotation.
+     */
+    boolean initClass() default true;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GrabConfig.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GrabConfig.java b/src/main/groovy/groovy/lang/GrabConfig.java
new file mode 100644
index 0000000..e6b8980
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GrabConfig.java
@@ -0,0 +1,117 @@
+/*
+ *  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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to modify the grape configuration for grab requests.
+ * <p>
+ * An example involving databases:
+ * <pre>
+ * {@code @Grab}('mysql:mysql-connector-java:5.1.6'),
+ * {@code @GrabConfig}(systemClassLoader=true)
+ * import groovy.sql.Sql
+ *
+ * def sql=Sql.newInstance("jdbc:mysql://localhost/test", "user", "password", "com.mysql.jdbc.Driver")
+ * println sql.firstRow('SELECT * FROM INFORMATION_SCHEMA.COLUMNS')
+ * </pre>
+ * Another example involving XStream:
+ * <pre>
+ * {@code @Grab}('com.thoughtworks.xstream:xstream:1.4.9'),
+ * {@code @Grab}('xpp3:xpp3_min:1.1.4c'),
+ * {@code @GrabConfig}(systemClassLoader=true, initContextClassLoader=true)
+ * import com.thoughtworks.xstream.*
+ *
+ * class Staff {
+ *     String firstname, lastname, position
+ * }
+ *
+ * def xstream = new XStream()
+ * def john1 = new Staff(firstname:'John',
+ *                      lastname:'Connor',
+ *                      position:'Resistance Leader')
+ *
+ * // write out to XML file
+ * new File("john.xml").withOutputStream { out ->
+ *     xstream.toXML(john1, out)
+ * }
+ *
+ * // now read back in
+ * def john2
+ * new File("john.xml").withInputStream { ins ->
+ *     john2 = xstream.fromXML(ins)
+ * }
+ *
+ * println john2.dump()
+ * </pre>
+ * <p>
+ * Further information about customising grape behavior can be found on the Grape documentation page:
+ * <a href="http://groovy-lang.org/grape.html">http://groovy-lang.org/grape.html</a>.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.TYPE})
+public @interface GrabConfig {
+    /**
+     * Set to true if you want to use the system classloader when loading the grape.
+     * This is normally only required when a core Java class needs to reference the grabbed
+     * classes, e.g. for a database driver accessed using DriverManager.
+     */
+    boolean systemClassLoader() default false;
+
+    /**
+     * Define any system properties which must be set before invoking the grab - useful for
+     * declaring SSL certificates or proxy settings. Currently, this only affects the generated
+     * class or script. You may need to also set those same properties for the Groovy compiler.
+     * For convenience, a String with comma separated name=value pairs
+     * can be used in addition to an array (using Groovy's literal list notation) of String name=value items.
+     * The single String shorthand form can't be used if value part of a property contains a comma.
+     *
+     * @since 2.4.5
+     */
+    String[] systemProperties() default "";
+
+    /**
+     * Set to true if you want the context classloader to be initialised to the classloader
+     * of the current class or script. This is useful for libraries or frameworks that assume
+     * that the context classloader has been set. But be careful when using this flag as your
+     * script or class might behave differently when called directly (from the command line or
+     * from an IDE) versus when called from within a container, e.g. a web container or a JEE container.
+     */
+    boolean initContextClassLoader() default false;
+
+    /**
+     * Set to false if you want to disable automatic downloading of locally missing jars.
+     */
+    boolean autoDownload() default true;
+
+    /**
+     * Set to true if you want to disable checksum checking.
+     */
+    boolean disableChecksums() default false;
+}
\ No newline at end of file