Groovy 2.2.0

groovy.lang
[Java] Class ExpandoMetaClass

java.lang.Object
  groovy.lang.MetaClassImpl
      groovy.lang.ExpandoMetaClass
All Implemented Interfaces:
GroovyObject

public class ExpandoMetaClass
extends MetaClassImpl

ExpandoMetaClass is a MetaClass that behaves like an Expando, allowing the addition or replacement of methods, properties and constructors on the fly.

Some examples of usage:

 // 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"
 

ExpandoMetaClass also supports a DSL/builder like notation to combine multiple definitions together. So instead of this:

 Number.metaClass.multiply = { Amount amount -> amount.times(delegate) }
 Number.metaClass.div =      { Amount amount -> amount.inverse().times(delegate) }
 
You can also now do this:
 Number.metaClass {
     multiply { Amount amount -> amount.times(delegate) }
     div      { Amount amount -> amount.inverse().times(delegate) }
 }
 

ExpandoMetaClass also supports runtime mixins. While @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.

 interface Vehicle {
     String getName()
 }

 // Category annotation style
 @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!"
 
As another example, consider the following class definitions:
 class Student {
     List schedule = []
     def addLecture(String lecture) { schedule << lecture }
 }

 class Worker {
     List schedule = []
     def addMeeting(String meeting) { schedule << meeting }
 }
 
We can mimic a form of multiple inheritance as follows:
 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
 }
 
Which outputs these lines when run:
 [Performance review with Boss]
 [Learn about Groovy Mixins]
 [Performance review with Boss]
 
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 schedule, it will be the schedule property (getSchedule method) from Worker that is used. The schedule property from Student will be shadowed but the mixedIn notation allows us to get to that too if we need as the last two lines show.

We can also be a little more dynamic and not require the CollegeStudent class to be defined at all, e.g.:

 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
 }
 
Which outputs this line when run:
 [Learn about Groovy Mixins, Performance review with Boss]
 
As another example, we can also define a no dup queue by mixing in some Queue and Set functionality as follows:
 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
 
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:
 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
 

Further details

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.

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.

Authors:
Graeme Rocher
Since:
1.5


Nested Class Summary
protected class ExpandoMetaClass.ExpandoMetaConstructor

Handles the ability to use the left shift operator to append new constructors

protected class ExpandoMetaClass.ExpandoMetaProperty

Instances of this class are returned when using the << left shift operator.

 
Field Summary
static String CONSTRUCTOR

static String STATIC_QUALIFIER

boolean inRegistry

 
Fields inherited from class MetaClassImpl
EMPTY_ARGUMENTS, INVOKE_METHOD_METHOD, METHOD_MISSING, PROPERTY_MISSING, STATIC_METHOD_MISSING, STATIC_PROPERTY_MISSING, getPropertyMethod, invokeMethodMethod, isGroovyObject, isMap, metaMethodIndex, registry, setPropertyMethod, theCachedClass, theClass
 
Constructor Summary
ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)

ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)

ExpandoMetaClass(Class theClass)

Constructs a new ExpandoMetaClass instance for the given class

ExpandoMetaClass(Class theClass, MetaMethod[] add)

ExpandoMetaClass(Class theClass, boolean register)

Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically

ExpandoMetaClass(Class theClass, boolean register, MetaMethod[] add)

ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit)

Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically

 
Method Summary
void addMixinClass(MixinInMetaClass mixin)

Object castToMixedType(Object obj, Class type)

protected void checkInitalised()

Registers a new bean property

CallSite createConstructorSite(CallSite site, Object[] args)

CallSite createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)

CallSite createPogoCallSite(CallSite site, Object[] args)

CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args)

CallSite createStaticSite(CallSite site, Object[] args)

ExpandoMetaClass define(Closure closure)

static void disableGlobally()

Call to disable the global use of ExpandoMetaClass

static void enableGlobally()

Call to enable global use of global use of ExpandoMetaClass within the registry.

MetaMethod findMixinMethod(String methodName, Class[] arguments)

List getExpandoMethods()

Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass

Collection getExpandoProperties()

Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass

Collection getExpandoSubclassMethods()

Class getJavaClass()

MetaClass getMetaClass()

MetaProperty getMetaProperty(String name)

List getMethods()

List getProperties()

Object getProperty(String property)

Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass)

Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass

Object getProperty(Object object, String name)

Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass

String getPropertyForSetter(String setterName)

protected Object getSubclassMetaMethods(String methodName)

@return The Java class enhanced by this MetaClass

boolean hasMetaMethod(String name, Class[] args)

Returns true if the name of the method specified and the number of arguments make it a javabean property

boolean hasMetaProperty(String name)

Determine if this method name suffix is a legitimate bean property name.

void initialize()

Object invokeConstructor(Object[] arguments)

Object invokeMethod(String name, Object args)

Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)

Object invokeStaticMethod(Object object, String methodName, Object[] arguments)

protected boolean isInitialized()

Checks if the meta class is initialized.

boolean isModified()

boolean isSetter(String name, CachedClass[] args)

static boolean isValidExpandoProperty(String property)

protected void onGetPropertyFoundInHierarchy(MetaMethod method)

protected void onInvokeMethodFoundInHierarchy(MetaMethod method)

protected void onSetPropertyFoundInHierarchy(MetaMethod method)

protected void onSuperMethodFoundInHierarchy(MetaMethod method)

protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property)

protected void performOperationOnMetaClass(ExpandoMetaClass.Callable c)

void refreshInheritedMethods(Set modifiedSuperExpandos)

void registerBeanProperty(String property, Object newValue)

void registerInstanceMethod(MetaMethod metaMethod)

Registers a new instance method for the given method name and closure on this MetaClass

void registerInstanceMethod(String name, Closure closure)

Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods

protected void registerStaticMethod(String name, Closure callable)

Registers a new static method for the given method name and closure on this MetaClass

protected void registerStaticMethod(String name, Closure callable, Class[] paramTypes)

void registerSubclassInstanceMethod(String name, Class klazz, Closure closure)

void registerSubclassInstanceMethod(MetaMethod metaMethod)

MetaMethod retrieveConstructor(Object[] args)

protected void setInitialized(boolean b)

void setMetaClass(MetaClass metaClass)

void setProperty(String property, Object newValue)

void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass)

 
Methods inherited from class MetaClassImpl
addMetaBeanProperty, addMetaMethod, addMetaMethodToIndex, addNewInstanceMethod, addNewStaticMethod, applyPropertyDescriptors, checkIfGroovyObjectMethod, checkInitalised, chooseMethod, clearInvocationCaches, createConstructorSite, createPogoCallCurrentSite, createPogoCallSite, createPojoCallSite, createStaticSite, dropMethodCache, dropStaticMethodCache, findMethodInClassHierarchy, findMixinMethod, findOwnMethod, findPropertyInClassHierarchy, getAdditionalMetaMethods, getAttribute, getAttribute, getAttribute, getClassInfo, getClassNode, getEffectiveGetMetaProperty, getMetaMethod, getMetaMethods, getMetaProperty, getMethodWithCaching, getMethodWithoutCaching, getMethods, getProperties, getProperty, getProperty, getRegistry, getStaticMetaMethod, getSubclassMetaMethods, getSuperClasses, getTheCachedClass, getTheClass, getVersion, hasProperty, incVersion, initialize, invokeConstructor, invokeMethod, invokeMethod, invokeMethod, invokeMissingMethod, invokeMissingProperty, invokeStaticMethod, invokeStaticMissingProperty, isGroovyObject, isInitialized, isModified, onGetPropertyFoundInHierarchy, onInvokeMethodFoundInHierarchy, onMixinMethodFound, onSetPropertyFoundInHierarchy, onSuperMethodFoundInHierarchy, onSuperPropertyFoundInHierarchy, pickMethod, respondsTo, respondsTo, retrieveConstructor, retrieveConstructor, retrieveStaticMethod, selectConstructorAndTransformArguments, setAttribute, setAttribute, setProperties, setProperty, setProperty, toString
 
Methods inherited from class Object
wait, wait, wait, equals, toString, hashCode, getClass, notify, notifyAll
 

Field Detail

CONSTRUCTOR

public static final String CONSTRUCTOR


STATIC_QUALIFIER

public static final String STATIC_QUALIFIER


inRegistry

public boolean inRegistry


 
Constructor Detail

ExpandoMetaClass

public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)


ExpandoMetaClass

public ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)


ExpandoMetaClass

public ExpandoMetaClass(Class theClass)
Constructs a new ExpandoMetaClass instance for the given class
Parameters:
theClass - The class that the MetaClass applies to


ExpandoMetaClass

public ExpandoMetaClass(Class theClass, MetaMethod[] add)


ExpandoMetaClass

public ExpandoMetaClass(Class theClass, boolean register)
Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
Parameters:
theClass - The class that the MetaClass applies to
register - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed


ExpandoMetaClass

public ExpandoMetaClass(Class theClass, boolean register, MetaMethod[] add)


ExpandoMetaClass

public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit)
Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
Parameters:
theClass - The class that the MetaClass applies to
register - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
allowChangesAfterInit - Should the meta class be modifiable after initialization. Default is false.


 
Method Detail

addMixinClass

public void addMixinClass(MixinInMetaClass mixin)


castToMixedType

public Object castToMixedType(Object obj, Class type)


checkInitalised

protected void checkInitalised()
Registers a new bean property
Parameters:
property - The property name
newValue - The properties initial value


createConstructorSite

public CallSite createConstructorSite(CallSite site, Object[] args)


createPogoCallCurrentSite

public CallSite createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)


createPogoCallSite

public CallSite createPogoCallSite(CallSite site, Object[] args)


createPojoCallSite

public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args)


createStaticSite

public CallSite createStaticSite(CallSite site, Object[] args)


define

public ExpandoMetaClass define(Closure closure)


disableGlobally

public static void disableGlobally()
Call to disable the global use of ExpandoMetaClass


enableGlobally

public static void enableGlobally()
Call to enable global use of global use of ExpandoMetaClass within the registry. This has the advantage that inheritance will function correctly, but has a higher memory usage on the JVM than normal Groovy


findMixinMethod

public MetaMethod findMixinMethod(String methodName, Class[] arguments)


getExpandoMethods

public List getExpandoMethods()
Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
Returns:
the expandoProperties


getExpandoProperties

public Collection getExpandoProperties()
Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
See Also:
MetaClassImpl.invokeStaticMethod


getExpandoSubclassMethods

public Collection getExpandoSubclassMethods()


getJavaClass

public Class getJavaClass()


getMetaClass

public MetaClass getMetaClass()


getMetaProperty

public MetaProperty getMetaProperty(String name)


getMethods

public List getMethods()


getProperties

public List getProperties()


getProperty

public Object getProperty(String property)


getProperty

public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass)
Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
See Also:
MetaClassImpl.getProperty


getProperty

public Object getProperty(Object object, String name)
Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass
See Also:
MetaClassImpl.setProperty


getPropertyForSetter

public String getPropertyForSetter(String setterName)


getSubclassMetaMethods

protected Object getSubclassMetaMethods(String methodName)
Returns:
The Java class enhanced by this MetaClass


hasMetaMethod

public boolean hasMetaMethod(String name, Class[] args)
Returns true if the name of the method specified and the number of arguments make it a javabean property
Parameters:
name - True if its a Javabean property
args - The arguments
Returns:
True if it is a javabean property method


hasMetaProperty

public boolean hasMetaProperty(String name)
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.


initialize

public void initialize()


invokeConstructor

public Object invokeConstructor(Object[] arguments)


invokeMethod

public Object invokeMethod(String name, Object args)


invokeMethod

public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)


invokeStaticMethod

public Object invokeStaticMethod(Object object, String methodName, Object[] arguments)


isInitialized

protected boolean isInitialized()
Checks if the meta class is initialized.
See Also:
MetaClassImpl.isInitialized


isModified

public boolean isModified()


isSetter

public boolean isSetter(String name, CachedClass[] args)


isValidExpandoProperty

public static boolean isValidExpandoProperty(String property)


onGetPropertyFoundInHierarchy

protected void onGetPropertyFoundInHierarchy(MetaMethod method)


onInvokeMethodFoundInHierarchy

protected void onInvokeMethodFoundInHierarchy(MetaMethod method)


onSetPropertyFoundInHierarchy

protected void onSetPropertyFoundInHierarchy(MetaMethod method)


onSuperMethodFoundInHierarchy

protected void onSuperMethodFoundInHierarchy(MetaMethod method)


onSuperPropertyFoundInHierarchy

protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property)


performOperationOnMetaClass

protected void performOperationOnMetaClass(ExpandoMetaClass.Callable c)


refreshInheritedMethods

public void refreshInheritedMethods(Set modifiedSuperExpandos)


registerBeanProperty

public void registerBeanProperty(String property, Object newValue)


registerInstanceMethod

public void registerInstanceMethod(MetaMethod metaMethod)
Registers a new instance method for the given method name and closure on this MetaClass
Parameters:
metaMethod


registerInstanceMethod

public void registerInstanceMethod(String name, Closure closure)
Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
Returns:
A list of MetaMethods
See Also:
MetaObjectProtocol.getMethods


registerStaticMethod

protected void registerStaticMethod(String name, Closure callable)
Registers a new static method for the given method name and closure on this MetaClass
Parameters:
name - The method name
callable - The callable Closure


registerStaticMethod

protected void registerStaticMethod(String name, Closure callable, Class[] paramTypes)


registerSubclassInstanceMethod

public void registerSubclassInstanceMethod(String name, Class klazz, Closure closure)


registerSubclassInstanceMethod

public void registerSubclassInstanceMethod(MetaMethod metaMethod)


retrieveConstructor

@Override
public MetaMethod retrieveConstructor(Object[] args)


setInitialized

protected void setInitialized(boolean b)


setMetaClass

public void setMetaClass(MetaClass metaClass)


setProperty

public void setProperty(String property, Object newValue)


setProperty

public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass)


 

Copyright &copy; 2003-2013 The Codehaus. All rights reserved.