http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/lang/ExpandoMetaClass.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/ExpandoMetaClass.java b/src/main/groovy/lang/ExpandoMetaClass.java deleted file mode 100644 index d56f738..0000000 --- a/src/main/groovy/lang/ExpandoMetaClass.java +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * 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/lang/ExpandoMetaClassCreationHandle.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java b/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java deleted file mode 100644 index 6e411a5..0000000 --- a/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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/lang/GString.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/GString.java b/src/main/groovy/lang/GString.java deleted file mode 100644 index 8323bf2..0000000 --- a/src/main/groovy/lang/GString.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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:[email protected]">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/lang/GeneratedGroovyProxy.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/GeneratedGroovyProxy.java b/src/main/groovy/lang/GeneratedGroovyProxy.java deleted file mode 100644 index 55b25b5..0000000 --- a/src/main/groovy/lang/GeneratedGroovyProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/lang/Grab.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/Grab.java b/src/main/groovy/lang/Grab.java deleted file mode 100644 index 2d789e6..0000000 --- a/src/main/groovy/lang/Grab.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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. 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/lang/GrabConfig.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/GrabConfig.java b/src/main/groovy/lang/GrabConfig.java deleted file mode 100644 index 7cbafb5..0000000 --- a/src/main/groovy/lang/GrabConfig.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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
