blob: 18b5c3950c1664c2c86de51c1b1671baad631e4e [file] [log] [blame]
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed 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.GroovyBugError;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.BytecodeHelper;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.reflection.*;
import org.codehaus.groovy.runtime.*;
import org.codehaus.groovy.runtime.metaclass.ConcurrentReaderHashMap;
import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
import org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod;
import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.wrappers.Wrapper;
import org.objectweb.asm.ClassVisitor;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Allows methods to be dynamically added to existing classes at runtime
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @author Guillaume Laforge
* @author Jochen Theodorou
* @author Graeme Rocher
*
* @version $Revision$
* @see groovy.lang.MetaClass
*/
public class MetaClassImpl implements MetaClass, MutableMetaClass {
private static final String CLOSURE_CALL_METHOD = "call";
private static final String CLOSURE_DO_CALL_METHOD = "doCall";
private static final String CLOSURE_CURRY_METHOD = "curry";
protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
protected static final String METHOD_MISSING = "methodMissing";
protected static final String PROPERTY_MISSING = "propertyMissing";
private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class};
private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class};
private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
private static final Reflector SKIP_REFLECTOR = new Reflector();
protected static final Logger LOG = Logger.getLogger(MetaClass.class.getName());
protected final Class theClass;
protected MetaClassRegistry registry;
protected final boolean isGroovyObject;
protected final boolean isMap;
private ClassNode classNode;
private final MethodIndex classMethodIndex = new MethodIndex();
private MethodIndex classMethodIndexForSuper;
private final MethodIndex classStaticMethodIndex = new MethodIndex();
private final Index classPropertyIndex = new MethodIndex();
private Index classPropertyIndexForSuper = new MethodIndex();
private final Map staticPropertyIndex = new HashMap();
private final Map listeners = new HashMap();
private final Map methodCache = new ConcurrentReaderHashMap();
private final Map staticMethodCache = new ConcurrentReaderHashMap();
private MetaMethod genericGetMethod;
private MetaMethod genericSetMethod;
private List constructors;
private final List allMethods = new ArrayList();
private List interfaceMethods;
private Reflector reflector;
private boolean initialized;
// we only need one of these that can be reused over and over.
private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
private static final MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new CachedClass[]{},null,0);
private static final Object[] EMPTY_ARGUMENTS = {};
private final List newGroovyMethodsList = new LinkedList();
public MetaClassImpl(final Class theClass) {
this.theClass = theClass;
this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
this.isMap = Map.class.isAssignableFrom(theClass);
this.registry = GroovySystem.getMetaClassRegistry();
}
public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
this(theClass);
this.registry = registry;
this.constructors = (List) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
final Constructor[] declaredConstructors = theClass.getDeclaredConstructors();
ArrayList array = new ArrayList(declaredConstructors.length);
for (int i = 0; i != declaredConstructors.length; ++i)
array.add(ReflectionCache.getCachedConstructor(declaredConstructors[i]));
return array;
}
});
}
/**
* @see MetaObjectProtocol#respondsTo(Object,String, Object[])
*/
public List respondsTo(Object obj,String name, Object[] argTypes) {
Class[] classes = castArgumentsToClassArray(argTypes);
MetaMethod m = getMetaMethod(name, classes);
List methods = new ArrayList();
if(m!= null) {
methods.add(m);
}
return methods;
}
private Class[] castArgumentsToClassArray(Object[] argTypes) {
if(argTypes == null)return new Class[0];
Class[] classes = new Class[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
Object argType = argTypes[i];
if(argType instanceof Class) {
classes[i] = (Class)argType;
}
else if(argType == null) {
classes[i] = null;
}
else {
// throw new IllegalArgumentException("Arguments to method [respondsTo] must be of type java.lang.Class!");
classes[i] = argType.getClass();
}
}
return classes;
}
/**
* @see MetaObjectProtocol#respondsTo(Object,String, Object[])
*/
public List respondsTo(final Object obj, final String name) {
return getMethods(getTheClass(),name,false);
}
/**
* @see MetaObjectProtocol#hasProperty(Object,String)
*/
public MetaProperty hasProperty(Object obj,String name) {
return getMetaProperty(name);
}
/**
* @see MetaObjectProtocol#getMetaProperty(String)
*/
public MetaProperty getMetaProperty(String name) {
Map propertyMap = classPropertyIndex.getNotNull(theClass);
if(propertyMap.containsKey(name)) {
return (MetaProperty)propertyMap.get(name);
}
else if(staticPropertyIndex.containsKey(name)) {
return (MetaProperty)staticPropertyIndex.get(name);
}
else {
propertyMap = classPropertyIndexForSuper.getNotNull(theClass);
return (MetaProperty)propertyMap.get(name);
}
}
/**
* @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
*/
public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) {
Class[] classes = castArgumentsToClassArray(argTypes);
return retrieveStaticMethod(name, classes);
}
/**
* @see MetaObjectProtocol#getMetaMethod(String, Object[])
*/
public MetaMethod getMetaMethod(String name, Object[] argTypes) {
Class[] classes = castArgumentsToClassArray(argTypes);
return pickMethod(name, classes);
}
public Class getTheClass() {
return this.theClass;
}
public boolean isGroovyObject(){
return isGroovyObject;
}
private void fillMethodIndex() {
if (theClass.isInterface()) {
// simplified version for interfaces (less inheritance)
LinkedList superClasses = new LinkedList();
superClasses.add(Object.class);
addMethods(Object.class);
Set interfaces = new HashSet();
interfaces.add(theClass);
makeInterfaceSet(theClass,interfaces);
inheritInterfaceMethods(interfaces);
Map theClassIndex = classMethodIndex.getNotNull(theClass);
Map objectIndex = classMethodIndex.getNotNull(Object.class);
copyNonPrivateMethods(objectIndex,theClassIndex);
classMethodIndexForSuper = classMethodIndex;
superClasses.addAll(interfaces);
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class c = (Class) iter.next();
classMethodIndex.put(c,theClassIndex);
if (c!=Object.class) addMethods(c);
}
} else {
LinkedList superClasses = getSuperClasses();
// let's add all the base class methods
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class c = (Class) iter.next();
addMethods(c);
}
Set interfaces = new HashSet();
makeInterfaceSet(theClass,interfaces);
inheritMethods(superClasses);
inheritInterfaceMethods(interfaces);
classMethodIndexForSuper = classMethodIndex.copy();
connectMultimethods(superClasses);
populateInterfaces(interfaces);
removeMultimethodsOverloadedWithPrivateMethods();
}
replaceWithMOPCalls();
}
private LinkedList getSuperClasses() {
LinkedList superClasses = new LinkedList();
if (theClass.isInterface()) {
superClasses.addFirst(Object.class);
} else {
for (Class c = theClass; c!= null; c = c.getSuperclass()) {
superClasses.addFirst(c);
}
if (theClass.isArray() && theClass!=Object[].class && !theClass.getComponentType().isPrimitive()) {
superClasses.addFirst(Object[].class);
}
}
return superClasses;
}
private void removeMultimethodsOverloadedWithPrivateMethods() {
MethodIndexAction mia = new MethodIndexAction() {
public List methodNameAction(Class clazz, String methodName, List methods) {
boolean hasPrivate=false;
for (Iterator iter = methods.iterator(); iter.hasNext();) {
MetaMethod method = (MetaMethod) iter.next();
if (method.isPrivate() && clazz == method.getDeclaringClass()) {
hasPrivate = true;
break;
}
}
if (!hasPrivate) return null;
// We have private methods for that name, so remove the
// multimethods. That is the same as in our index for
// super, so just copy the list from there. It is not
// possible to use a pointer here, because the methods
// in the index for super are replaced later by MOP
// methods like super$5$foo
methods.clear();
methods.addAll((Collection) ((Map) classMethodIndexForSuper.getNotNull(clazz)).get(methodName));
return methods;
}
public boolean replaceMethodList() {return false;}
};
mia.iterate(classMethodIndex);
}
private void replaceWithMOPCalls() {
// no MOP methods if not a child of GroovyObject
if (!GroovyObject.class.isAssignableFrom(theClass)) return;
final Map mainClassMethodIndex = (Map) classMethodIndex.getNotNull(theClass);
class MOPIter extends MethodIndexAction {
boolean useThis;
public boolean skipClass(Class clazz) {
return !useThis && clazz==theClass;
}
public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {
String mopName = ReflectionCache.getMOPMethodName(method.getDeclaringClass(), methodName,useThis);
List matches = (List) mainClassMethodIndex.get(mopName);
if (matches==null) {
newList.add(method);
return;
}
matches = new ArrayList(matches);
MetaMethod matchingMethod = removeMatchingMethod(matches,method);
if (matchingMethod==null) {
newList.add(method);
} else {
newList.add(matchingMethod);
}
}
}
MOPIter iter = new MOPIter();
// replace all calls for super with the correct MOP method
iter.useThis = false;
iter.iterate(classMethodIndexForSuper);
// replace all calls for this with the correct MOP method
iter.useThis = true;
iter.iterate(classMethodIndex);
}
private void inheritInterfaceMethods(Set interfaces) {
// add methods declared by DGM for interfaces
List methods = ((MetaClassRegistryImpl)registry).getInstanceMethods();
for (Iterator iter = methods.iterator(); iter.hasNext();) {
CachedMethod element = (CachedMethod) iter.next();
Class dgmClass = element.getParameterTypes()[0].cachedClass;
if (!interfaces.contains(dgmClass)) continue;
NewInstanceMetaMethod method = new NewInstanceMetaMethod(createMetaMethod(element));
if (! newGroovyMethodsList.contains(method)){
newGroovyMethodsList.add(method);
}
Map methodIndex = classMethodIndex.getNotNull(theClass);
List list = (List) methodIndex.get(method.getName());
if (list == null) {
list = new ArrayList();
methodIndex.put(method.getName(), list);
list.add(method);
} else {
addMethodToList(list,method);
}
}
methods = ((MetaClassRegistryImpl)registry).getStaticMethods();
for (Iterator iter = methods.iterator(); iter.hasNext();) {
CachedMethod element = (CachedMethod) iter.next();
Class dgmClass = element.getParameterTypes()[0].cachedClass;
if (!interfaces.contains(dgmClass)) continue;
addNewStaticMethod(element.cachedMethod);
}
}
private void populateInterfaces(Set interfaces){
Map currentIndex = classMethodIndex.getNotNull(theClass);
Map index = new HashMap();
copyNonPrivateMethods(currentIndex,index);
for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
Class iClass = (Class) iter.next();
Map methodIndex = classMethodIndex.getNullable(iClass);
if (methodIndex==null || methodIndex.isEmpty()) {
classMethodIndex.put(iClass,index);
continue;
}
copyNonPrivateMethods(currentIndex,methodIndex);
}
}
private static void makeInterfaceSet(Class c, Set s) {
if (c==null) return;
Class[] interfaces = c.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (!s.contains(interfaces[i])) {
s.add(interfaces[i]);
makeInterfaceSet(interfaces[i],s);
}
}
makeInterfaceSet(c.getSuperclass(),s);
}
private void copyNonPrivateMethods(Map from, Map to) {
for (Iterator iterator = from.entrySet().iterator(); iterator.hasNext();) {
Map.Entry element = (Map.Entry) iterator.next();
List oldList = (List) element.getValue();
List newList = (List) to.get(element.getKey());
if (newList==null) {
to.put(element.getKey(),new ArrayList(oldList));
} else {
addNonPrivateMethods(newList,oldList);
}
}
}
private void connectMultimethods(List superClasses){
superClasses = DefaultGroovyMethods.reverse(superClasses);
Map last = null;
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class c = (Class) iter.next();
Map methodIndex = classMethodIndex.getNullable(c);
if (methodIndex==last) continue;
if (last!=null) copyNonPrivateMethods(last,methodIndex);
last = methodIndex;
}
}
private void inheritMethods(Collection superClasses){
Map last = null;
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class c = (Class) iter.next();
Map methodIndex = classMethodIndex.getNullable(c);
if (last!=null) {
if (methodIndex.isEmpty()) {
classMethodIndex.put(c,last);
continue;
}
copyNonPrivateMethods(last,methodIndex);
}
last = methodIndex;
}
}
private void addNonPrivateMethods(List newList, List oldList) {
for (Iterator iter = oldList.iterator(); iter.hasNext();) {
MetaMethod element = (MetaMethod) iter.next();
if (element.isPrivate()) continue;
addMethodToList(newList,element);
}
}
/**
* @return all the normal instance methods avaiable on this class for the
* given name
*/
private List getMethods(Class sender, String name, boolean isCallToSuper) {
Map methodIndex;
if (isCallToSuper) {
methodIndex = (Map) classMethodIndexForSuper.getNullable(sender);
} else {
methodIndex = (Map) classMethodIndex.getNullable(sender);
}
List answer;
if (methodIndex!=null) {
answer = (List) methodIndex.get(name);
if (answer == null) answer = Collections.EMPTY_LIST;
} else {
answer = Collections.EMPTY_LIST;
}
if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) {
List used = GroovyCategorySupport.getCategoryMethods(sender, name);
if (used != null) {
answer = new ArrayList(answer);
for (Iterator iter = used.iterator(); iter.hasNext();) {
MetaMethod element = (MetaMethod) iter.next();
removeMatchingMethod(answer,element);
}
answer.addAll(used);
}
}
return answer;
}
/**
* @return all the normal static methods avaiable on this class for the
* given name
*/
private List getStaticMethods(Class sender, String name) {
Map methodIndex = (Map) classStaticMethodIndex.getNullable(sender);
if (methodIndex == null) return Collections.EMPTY_LIST;
List answer = (List) methodIndex.get(name);
if (answer == null) return Collections.EMPTY_LIST;
return answer;
}
public boolean isModified() {
return false; // MetaClassImpl not designed for modification, just return false
}
public void addNewInstanceMethod(Method method) {
addNewInstanceMethod(method, ReflectionCache.getCachedMethod(method).getParameterTypes());
}
public void addNewInstanceMethod(Method method, CachedClass [] pt) {
NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method, pt));
if (! newGroovyMethodsList.contains(newMethod)){
newGroovyMethodsList.add(newMethod);
addMetaMethod(newMethod);
}
}
public void addNewStaticMethod(Method method) {
addNewStaticMethod(method, ReflectionCache.getCachedMethod(method).getParameterTypes());
}
public void addNewStaticMethod(Method method, CachedClass [] pt) {
NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(CachedMethod.find(method)));
if (! newGroovyMethodsList.contains(newMethod)){
newGroovyMethodsList.add(newMethod);
addMetaMethod(newMethod);
}
}
private void unwrap(Object[] arguments) {
//
// Temp code to ignore wrapped parameters
// The New MOP will deal with these properly
//
for (int i = 0; i != arguments.length; i++) {
if (arguments[i] instanceof Wrapper) {
arguments[i] = ((Wrapper)arguments[i]).unwrap();
}
}
}
public Object invokeMethod(Object object, String methodName, Object arguments) {
if (arguments == null) {
return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
}
if (arguments instanceof Tuple) {
Tuple tuple = (Tuple) arguments;
return invokeMethod(object, methodName, tuple.toArray());
}
if (arguments instanceof Object[]) {
return invokeMethod(object, methodName, (Object[])arguments);
}
else {
return invokeMethod(object, methodName, new Object[]{arguments});
}
}
public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
return invokeMissingMethod(instance, methodName, arguments, null);
}
public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
if(instance.getClass() != Class.class) {
if(isGetter) {
MetaMethod propertyMissing = getMetaMethod(PROPERTY_MISSING, GETTER_MISSING_ARGS);
if(propertyMissing!=null) {
return propertyMissing.invoke(instance, new Object[]{propertyName});
}
}
else {
MetaMethod propertyMissing = getMetaMethod(PROPERTY_MISSING, SETTER_MISSING_ARGS);
if(propertyMissing!=null) {
return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
}
}
}
throw new MissingPropertyException(propertyName, theClass);
}
private Object invokeMissingMethod(Object instance, String methodName, Object[] arguments, RuntimeException original) {
MetaMethod method = getMetaMethod(METHOD_MISSING, METHOD_MISSING_ARGS);
if(method!=null) {
return method.invoke(instance,new Object[]{methodName, arguments});
}
else if(original!=null) throw original;
else throw new MissingMethodException(methodName, theClass, arguments, false);
}
/**
* Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up
* "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException
*
* @param instance The instance
* @param propertyName The name of the property
* @param optionalValue The value in the case of a setter
* @param isGetter True if its a getter
* @return The value in the case of a getter or a MissingPropertyException
*/
protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class)instance) : this;
if(isGetter) {
MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
if(propertyMissing!=null) {
return propertyMissing.invoke(instance, new Object[]{propertyName});
}
}
else {
MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
if(propertyMissing!=null) {
return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
}
}
if(instance instanceof Class) {
throw new MissingPropertyException(propertyName, (Class)instance);
}
throw new MissingPropertyException(propertyName, theClass);
}
/**
* Invokes the given method on the object.
* TODO: should this be deprecated? If so, we have to propogate to many places.
*/
public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
return invokeMethod(theClass,object,methodName,originalArguments,false,false);
}
/**
* Invokes the given method on the object.
*
*/
public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
checkInitalised();
if (object == null) {
throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
}
if (LOG.isLoggable(Level.FINER)){
MetaClassHelper.logMethodCall(object, methodName, originalArguments);
}
final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
unwrap(arguments);
MetaMethod method = getMethodWithCaching(sender, methodName, argClasses, isCallToSuper);
if (method==null && arguments.length==1 && arguments[0] instanceof List) {
Object[] newArguments = ((List) arguments[0]).toArray();
Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
method = getMethodWithCaching(sender, methodName, newArgClasses, isCallToSuper);
if (method!=null) {
method = new TransformMetaMethod(method) {
public Object invoke(Object object, Object[] arguments) {
Object firstArgument = arguments[0];
List list = (List) firstArgument;
arguments = list.toArray();
return super.invoke(object, arguments);
}
};
}
}
final boolean isClosure = object instanceof Closure;
if (isClosure) {
final Closure closure = (Closure) object;
final Object owner = closure.getOwner();
if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) {
final Class objectClass = object.getClass();
if (objectClass ==MethodClosure.class) {
final MethodClosure mc = (MethodClosure) object;
methodName = mc.getMethod();
final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
return ownerMetaClass.invokeMethod(ownerClass,owner,methodName,arguments,false,false);
} else if (objectClass ==CurriedClosure.class) {
final CurriedClosure cc = (CurriedClosure) object;
// change the arguments for an uncurried call
final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
return ownerMetaClass.invokeMethod(owner,methodName,curriedArguments);
}
} else if (CLOSURE_CURRY_METHOD.equals(methodName)) {
return closure.curry(arguments);
}
final Object delegate = closure.getDelegate();
final boolean isClosureNotOwner = owner != closure;
final int resolveStrategy = closure.getResolveStrategy();
switch(resolveStrategy) {
case Closure.TO_SELF:
method = closure.getMetaClass().pickMethod(methodName, argClasses);
if(method !=null)return method.invoke(closure, arguments);
break;
case Closure.DELEGATE_ONLY:
if (method==null && delegate!=closure && delegate!=null) {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
method = delegateMetaClass.pickMethod(methodName,argClasses);
if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
else if (delegate!=closure && (delegate instanceof GroovyObject)) {
return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
}
}
break;
case Closure.OWNER_ONLY:
if (method==null && owner!=closure) {
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
}
break;
case Closure.DELEGATE_FIRST:
if (method==null && delegate!=closure && delegate!=null) {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
method = delegateMetaClass.pickMethod(methodName,argClasses);
if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
}
if (method==null && owner!=closure) {
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
method = ownerMetaClass.pickMethod(methodName,argClasses);
if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
}
if (method==null && resolveStrategy != Closure.TO_SELF) {
// still no methods found, test if delegate or owner are GroovyObjects
// and invoke the method on them if so.
MissingMethodException last = null;
if (delegate!=closure && (delegate instanceof GroovyObject)) {
try {
return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
} catch (MissingMethodException mme) {
if (last==null) last = mme;
}
}
if (isClosureNotOwner && (owner instanceof GroovyObject)) {
try {
return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
} catch (MissingMethodException mme) {
last = mme;
}
}
if (last!=null) return invokeMissingMethod(object, methodName, originalArguments, last);
}
break;
default:
if (method==null && owner!=closure) {
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
method = ownerMetaClass.pickMethod(methodName,argClasses);
if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
}
if (method==null && delegate!=closure && delegate!=null) {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
method = delegateMetaClass.pickMethod(methodName,argClasses);
if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
}
if (method==null && resolveStrategy != Closure.TO_SELF) {
// still no methods found, test if delegate or owner are GroovyObjects
// and invoke the method on them if so.
MissingMethodException last = null;
if (isClosureNotOwner && (owner instanceof GroovyObject)) {
try {
return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
} catch (MissingMethodException mme) {
if (last==null) last = mme;
}
}
if (delegate!=closure && (delegate instanceof GroovyObject)) {
try {
return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
} catch (MissingMethodException mme) {
last = mme;
}
}
if (last!=null) return invokeMissingMethod(object, methodName, originalArguments, last);
}
}
}
if (method != null) {
return MetaClassHelper.doMethodInvoke(object, method, arguments);
} else {
// if no method was found, try to find a closure defined as a field of the class and run it
Object value = null;
try {
value = this.getProperty(object, methodName);
} catch (MissingPropertyException mpe) {
// ignore
}
if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
Closure closure = (Closure) value;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(),closure, CLOSURE_DO_CALL_METHOD,originalArguments,false,fromInsideClass);
}
return invokeMissingMethod(object, methodName, originalArguments);
}
}
private MetaClass lookupObjectMetaClass(Object object) {
if (object instanceof GroovyObject) {
GroovyObject go = (GroovyObject) object;
return go.getMetaClass();
}
Class ownerClass = object.getClass();
if (ownerClass==Class.class) ownerClass = (Class) object;
MetaClass metaClass = registry.getMetaClass(ownerClass);
return metaClass;
}
private Object invokeMethodOnGroovyObject(String methodName, Object[] originalArguments, Object owner) {
GroovyObject go = (GroovyObject) owner;
return go.invokeMethod(methodName,originalArguments);
}
private MetaMethod lookupMetaMethodFromObject(String methodName, Class[] argClasses, Object instance) {
MetaMethod method;
final Class ownerClass = instance instanceof Class ? (Class)instance : instance.getClass();
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
method = ownerMetaClass.pickMethod(methodName,argClasses);
return method;
}
private Object invokeCurriedClosure(Object object, String methodName, Object[] arguments, Object owner) {
CurriedClosure cc = (CurriedClosure) object;
// change the arguments for an uncurried call
Object[] curriedArguments = cc.getUncurriedArguments(arguments);
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
return ownerMetaClass.invokeMethod(owner,methodName,curriedArguments);
}
private Object invokeMethodClosure(Object object, Object[] arguments, Object owner) {
String methodName;
MethodClosure mc = (MethodClosure) object;
methodName = mc.getMethod();
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
return ownerMetaClass.invokeMethod(owner.getClass(),owner,methodName,arguments,false,false);
}
public MetaMethod getMethodWithCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
// lets try use the cache to find the method
if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) {
return getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
} else {
MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments, isCallToSuper);
MetaMethod method = (MetaMethod) methodCache.get(methodKey);
if (method == null) {
method = getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
cacheInstanceMethod(methodKey, method);
}
return method;
}
}
protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
if (method != null && method.isCacheable()) {
methodCache.put(key, method);
}
}
protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
if (method != null && method.isCacheable()) {
staticMethodCache.put(key, method);
}
}
public Constructor retrieveConstructor(Class[] arguments) {
CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments, false);
if (constructor != null) {
return constructor.cachedConstructor;
}
constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments, true);
if (constructor != null) {
return constructor.cachedConstructor;
}
return null;
}
public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments, false);
MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
if (method == null) {
method = pickStaticMethod(methodName, arguments);
cacheStaticMethod(methodKey.createCopy(), method);
}
return method;
}
public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
MetaMethod method = null;
List methods = getMethods(sender,methodName,isCallToSuper);
if (methods!=null && !methods.isEmpty()) {
method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
}
return method;
}
public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
checkInitalised();
if (LOG.isLoggable(Level.FINER)){
MetaClassHelper.logMethodCall(object, methodName, arguments);
}
final Class sender = object instanceof Class ? (Class)object : object.getClass();
if (sender!=theClass) {
MetaClass mc = registry.getMetaClass(sender);
return mc.invokeStaticMethod(sender,methodName,arguments);
}
if (sender==Class.class) {
return invokeMethod(object,methodName,arguments);
}
if (arguments==null) arguments = EMPTY_ARGUMENTS;
Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
MetaMethod method = retrieveStaticMethod(methodName, argClasses);
// lets try use the cache to find the method
if (method != null) {
unwrap(arguments);
return MetaClassHelper.doMethodInvoke(object, method, arguments);
}
Object prop = null;
try {
prop = getProperty(theClass, theClass, methodName, false, false);
} catch (MissingPropertyException mpe) {
// ignore
}
if (prop instanceof Closure) {
return invokeStaticClosureProperty(arguments, prop);
}
Object[] originalArguments = (Object[]) arguments.clone();
unwrap(arguments);
Class superClass = sender.getSuperclass();
while(superClass != Object.class && superClass != null) {
MetaClass mc = registry.getMetaClass(superClass);
method = mc.getStaticMetaMethod(methodName, argClasses);
if(method!=null)return MetaClassHelper.doMethodInvoke(object, method, arguments);
try {
prop = mc.getProperty(superClass, superClass, methodName, false, false);
} catch (MissingPropertyException mpe) {
// ignore
}
if (prop instanceof Closure) {
return invokeStaticClosureProperty(originalArguments, prop);
}
superClass = superClass.getSuperclass();
}
return invokeStaticMissingMethod(sender, methodName, arguments);
}
private Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) {
Closure closure = (Closure) prop;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(),closure, CLOSURE_DO_CALL_METHOD,originalArguments,false,false);
}
private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) {
MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, METHOD_MISSING_ARGS);
if(metaMethod!=null) {
return metaMethod.invoke(sender, new Object[]{methodName, arguments});
}
throw new MissingMethodException(methodName, sender, arguments, true);
}
private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
MetaMethod method = null;
List methods = getStaticMethods(theClass,methodName);
if (!methods.isEmpty()) {
method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
}
if (method == null && theClass != Class.class) {
MetaClass classMetaClass = registry.getMetaClass(Class.class);
method = classMetaClass.pickMethod(methodName, arguments);
}
if (method == null) {
method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
}
return method;
}
/**
* Warning, this method will be removed
* @deprecated use invokeConstructor instead
*/
public Object invokeConstructorAt(Class at, Object[] arguments) {
return invokeConstructor(arguments);
}
public Object invokeConstructor(Object[] arguments) {
return invokeConstructor(theClass,arguments);
}
public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
//TODO: that is just a quick prototype, not the real thing!
if (numberOfCosntructors != constructors.size()) {
throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for "+
this.theClass.getName()+" do not match. Expected "+numberOfCosntructors+" but got "+constructors.size());
}
if (arguments==null) arguments = EMPTY_ARGUMENTS;
Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
unwrap(arguments);
CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, false);
if (constructor == null) {
constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, true);
}
if (constructor==null) {
throw new GroovyRuntimeException(
"Could not find matching constructor for: "
+ theClass.getName()
+ "("+InvokerHelper.toTypeString(arguments)+")");
}
List l = new ArrayList(constructors);
Comparator comp = new Comparator() {
public int compare(Object arg0, Object arg1) {
CachedConstructor c0 = (CachedConstructor) arg0;
CachedConstructor c1 = (CachedConstructor) arg1;
String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes());
String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes());
return descriptor0.compareTo(descriptor1);
}
};
Collections.sort(l,comp);
int found = -1;
for (int i=0; i<l.size(); i++) {
if (l.get(i)!=constructor) continue;
found = i;
break;
}
// NOTE: must be changed to "1 |" if constructor was vargs
return 0 | (found << 8);
}
/**
* checks if the initialisation of the class id complete.
* This method should be called as a form of assert, it is no
* way to test if there is still initialisation work to be done.
* Such logic must be implemented in a different way.
* @throws IllegalStateException if the initialisation is incomplete yet
*/
protected void checkInitalised() {
if (!isInitialized())
throw new IllegalStateException(
"initialize must be called for meta " +
"class of "+ theClass +
"("+this.getClass() + ") " +
"to complete initialisation process " +
"before any invocation or field/property " +
"access can be done");
}
private Object invokeConstructor(Class at, Object[] arguments) {
checkInitalised();
if (arguments==null) arguments = EMPTY_ARGUMENTS;
Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
unwrap(arguments);
CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, false);
if (constructor != null) {
return doConstructorInvoke(at, constructor, arguments, true);
}
constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, true);
if (constructor != null) {
return doConstructorInvoke(at, constructor, arguments, true);
}
if (arguments.length == 1) {
Object firstArgument = arguments[0];
if (firstArgument instanceof Map) {
constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
if (constructor != null) {
Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true);
setProperties(bean, ((Map) firstArgument));
return bean;
}
}
}
throw new GroovyRuntimeException(
"Could not find matching constructor for: "
+ theClass.getName()
+ "("+InvokerHelper.toTypeString(arguments)+")");
}
/**
* Sets a number of bean properties from the given Map where the keys are
* the String names of properties and the values are the values of the
* properties to set
*/
public void setProperties(Object bean, Map map) {
checkInitalised();
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
String key = entry.getKey().toString();
Object value = entry.getValue();
setProperty(bean, key, value);
}
}
/**
* @return the given property's value on the object
*/
public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
//----------------------------------------------------------------------
// handling of static
//----------------------------------------------------------------------
boolean isStatic = theClass != Class.class && object instanceof Class;
if (isStatic && object != theClass) {
MetaClass mc = registry.getMetaClass((Class) object);
return mc.getProperty(sender,object,name,useSuper,false);
}
checkInitalised();
//----------------------------------------------------------------------
// turn getProperty on a Map to get on the Map itself
//----------------------------------------------------------------------
if(!isStatic && this.isMap) {
return ((Map)object).get(name);
}
MetaMethod method = null;
Object[] arguments = EMPTY_ARGUMENTS;
//----------------------------------------------------------------------
// getter
//----------------------------------------------------------------------
MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
if (mp != null) {
if (mp instanceof MetaBeanProperty) {
MetaBeanProperty mbp = (MetaBeanProperty) mp;
method = mbp.getGetter();
mp = mbp.getField();
}
}
// check for a category method named like a getter
if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
String getterName = "get"+MetaClassHelper.capitalize(name);
MetaMethod categoryMethod = getCategoryMethodGetter(sender,getterName,false);
if (categoryMethod!=null) method = categoryMethod;
}
//----------------------------------------------------------------------
// field
//----------------------------------------------------------------------
if (method==null && mp!=null) {
return mp.getProperty(object);
}
//----------------------------------------------------------------------
// generic get method
//----------------------------------------------------------------------
// check for a generic get method provided through a category
if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
method = getCategoryMethodGetter(sender,"get",true);
if (method!=null) arguments = new Object[]{name};
}
// the generic method is valid, if available (!=null), if static or
// if it is not static and we do no static access
if (method==null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
arguments = new Object[]{ name };
method = genericGetMethod;
}
//----------------------------------------------------------------------
// special cases
//----------------------------------------------------------------------
if (method==null) {
/** todo these special cases should be special MetaClasses maybe */
if (theClass != Class.class && object instanceof Class) {
MetaClass mc = registry.getMetaClass(Class.class);
return mc.getProperty(Class.class,object,name,useSuper,false);
} else if (object instanceof Collection) {
return DefaultGroovyMethods.getAt((Collection) object, name);
} else if (object instanceof Object[]) {
return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
} else {
MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
if (addListenerMethod != null) {
//TODO: one day we could try return the previously registered Closure listener for easy removal
return null;
}
}
} else {
//----------------------------------------------------------------------
// executing the getter method
//----------------------------------------------------------------------
return MetaClassHelper.doMethodInvoke(object,method,arguments);
}
//----------------------------------------------------------------------
// error due to missing method/field
//----------------------------------------------------------------------
if(isStatic || object instanceof Class)
return invokeStaticMissingProperty(object, name, null, true);
else
return invokeMissingProperty(object, name, null, true);
}
private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
if (possibleGenericMethods != null) {
for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
MetaMethod mmethod = (MetaMethod) iter.next();
CachedClass[] paramTypes = mmethod.getParameterTypes();
if (useLongVersion) {
if (paramTypes.length==1 && paramTypes[0].cachedClass == String.class) {
return mmethod;
}
} else {
if (paramTypes.length==0) return mmethod;
}
}
}
return null;
}
private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
if (possibleGenericMethods != null) {
for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
MetaMethod mmethod = (MetaMethod) iter.next();
CachedClass[] paramTypes = mmethod.getParameterTypes();
if (useLongVersion) {
if (paramTypes.length==2 && paramTypes[0].cachedClass == String.class) {
return mmethod;
}
} else {
if (paramTypes.length==1) return mmethod;
}
}
}
return null;
}
/**
* Get all the properties defined for this type
* @return a list of MetaProperty objects
*/
public List getProperties() {
checkInitalised();
Map propertyMap = classPropertyIndex.getNullable(theClass);
// simply return the values of the metaproperty map as a List
List ret = new ArrayList(propertyMap.size());
for (Iterator iter = propertyMap.values().iterator(); iter.hasNext();) {
MetaProperty element = (MetaProperty) iter.next();
if (element instanceof MetaFieldProperty) continue;
// filter out DGM beans
if (element instanceof MetaBeanProperty) {
MetaBeanProperty mp = (MetaBeanProperty) element;
boolean setter = true;
boolean getter = true;
if (mp.getGetter()==null || mp.getGetter() instanceof NewInstanceMetaMethod) {
getter=false;
}
if (mp.getSetter()==null || mp.getSetter() instanceof NewInstanceMetaMethod) {
setter=false;
}
if (!setter && !getter) continue;
if (!setter && mp.getSetter()!=null) {
element = new MetaBeanProperty(mp.getName(),mp.getType(),mp.getGetter(),null);
}
if (!getter && mp.getGetter()!=null) {
element = new MetaBeanProperty(mp.getName(),mp.getType(), null, mp.getSetter());
}
}
ret.add(element);
}
return ret;
}
private MetaMethod findPropertyMethod(List methods, boolean isGetter) {
LinkedList ret = new LinkedList();
for (Iterator iter = methods.iterator(); iter.hasNext();) {
MetaMethod element = (MetaMethod) iter.next();
if ( !isGetter &&
//(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
element.getParameterTypes().length == 1)
{
ret.add(element);
}
if ( isGetter &&
!(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
element.getParameterTypes().length == 0)
{
ret.add(element);
}
}
if (ret.isEmpty()) return null;
if (ret.size() == 1) return (MetaMethod) ret.getFirst();
// we found multiple matching methods
// this is a problem, because we can use only one
// if it is a getter, then use the most general return
// type to decide which method to use. If it is a setter
// we use the type of the first parameter
MetaMethod method = null;
int distance = -1;
for (Iterator iter = ret.iterator(); iter.hasNext();) {
MetaMethod element = (MetaMethod) iter.next();
Class c;
if (isGetter) {
c = element.getReturnType();
} else {
c = element.getParameterTypes()[0].cachedClass;
}
int localDistance = distanceToObject(c);
//TODO: maybe implement the case localDistance==distance
if (distance==-1 || distance>localDistance) {
distance = localDistance;
method = element;
}
}
return method;
}
private static int distanceToObject(Class c) {
int count;
for (count=0; c!=null; count++) {
c=c.getSuperclass();
}
return count;
}
/**
* This will build up the property map (Map of MetaProperty objects, keyed on
* property name).
*/
private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
if (theClass.isInterface()) {
LinkedList superClasses = new LinkedList();
superClasses.add(Object.class);
Set interfaces = new HashSet();
makeInterfaceSet(theClass,interfaces);
interfaces.add(theClass);
classPropertyIndexForSuper = classPropertyIndex;
for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
Class iclass = (Class) interfaceIter.next();
Map iPropertyIndex = classPropertyIndex.getNotNull(theClass);
addFields(iclass,iPropertyIndex);
classPropertyIndex.put(iclass,iPropertyIndex);
}
classPropertyIndex.put(Object.class, classPropertyIndex.getNotNull(theClass));
applyPropertyDescriptors(propertyDescriptors);
applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex);
makeStaticPropertyIndex();
} else {
LinkedList superClasses = getSuperClasses();
Set interfaces = new HashSet();
makeInterfaceSet(theClass,interfaces);
// if this an Array, then add the special read-only "length" property
if (theClass.isArray()) {
Map map = new HashMap();
map.put("length", arrayLengthProperty);
classPropertyIndex.put(theClass,map);
}
inheritStaticInterfaceFields(superClasses, interfaces);
inheritFields(superClasses);
applyPropertyDescriptors(propertyDescriptors);
applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex);
applyStrayPropertyMethods(superClasses,classMethodIndexForSuper,classPropertyIndexForSuper);
copyClassPropertyIndexForSuper(classPropertyIndexForSuper);
makeStaticPropertyIndex();
}
}
private void makeStaticPropertyIndex() {
Map propertyMap = classPropertyIndex.getNotNull(theClass);
for (Iterator iter = propertyMap.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
MetaProperty mp = (MetaProperty) entry.getValue();
if (mp instanceof MetaFieldProperty) {
MetaFieldProperty mfp = (MetaFieldProperty) mp;
if (!mfp.isStatic()) continue;
} else if (mp instanceof MetaBeanProperty) {
MetaProperty result = establishStaticMetaProperty(mp);
if(result == null)continue;
else {
mp = result;
}
} else {
continue; // ignore all other types
}
staticPropertyIndex.put(entry.getKey(),mp);
}
}
private MetaProperty establishStaticMetaProperty(MetaProperty mp) {
MetaBeanProperty mbp = (MetaBeanProperty) mp;
MetaProperty result = null;
final MetaMethod getterMethod = mbp.getGetter();
final MetaMethod setterMethod = mbp.getSetter();
final MetaFieldProperty metaField = mbp.getField();
boolean getter = getterMethod ==null || getterMethod.isStatic();
boolean setter = setterMethod ==null || setterMethod.isStatic();
boolean field = metaField ==null || metaField.isStatic();
if (!getter && !setter && !field) {
return result;
} else {
final String propertyName = mbp.getName();
final Class propertyType = mbp.getType();
if (setter && getter) {
if (field) {
result = mbp; // nothing to do
} else {
result = new MetaBeanProperty(propertyName, propertyType, getterMethod, setterMethod);
}
} else if (getter && !setter) {
if (getterMethod ==null) {
result = metaField;
} else {
MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, getterMethod,null);
if (field) newmp.setField(metaField);
result = newmp;
}
} else if (setter && !getter) {
if (setterMethod ==null) {
result = metaField;
} else {
MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType,null, setterMethod);
if (field) newmp.setField(metaField);
result = newmp;
}
} else if (field) {
result = metaField;
}
}
return result;
}
private void copyClassPropertyIndexForSuper(Index dest) {
for (Iterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
Map newVal = new HashMap((Map)entry.getValue());
dest.put((Class) entry.getKey(),newVal);
}
}
private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
Class iclass = (Class) interfaceIter.next();
Map iPropertyIndex = classPropertyIndex.getNotNull(iclass);
addFields(iclass,iPropertyIndex);
for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
Class sclass = (Class) classIter.next();
if (! iclass.isAssignableFrom(sclass)) continue;
Map sPropertyIndex = classPropertyIndex.getNotNull(sclass);
copyNonPrivateFields(iPropertyIndex,sPropertyIndex);
}
}
}
private void inheritFields(LinkedList superClasses) {
Map last = null;
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class klass = (Class) iter.next();
Map propertyIndex = classPropertyIndex.getNotNull(klass);
if (last != null) {
copyNonPrivateFields(last,propertyIndex);
}
last = propertyIndex;
addFields(klass,propertyIndex);
}
}
private void addFields(final Class klass, Map propertyIndex) {
Field[] fields = (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return klass.getDeclaredFields();
}
});
for(int i = 0; i < fields.length; i++) {
MetaFieldProperty mfp = MetaFieldProperty.create(fields[i]);
propertyIndex.put(fields[i].getName(), mfp);
}
}
private void copyNonPrivateFields(Map from, Map to) {
for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue();
if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
to.put(entry.getKey(),mfp);
}
}
private void applyStrayPropertyMethods(LinkedList superClasses, Index classMethodIndex, Index classPropertyIndex) {
// now look for any stray getters that may be used to define a property
for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
Class klass = (Class) iter.next();
Map methodIndex = (Map) classMethodIndex.getNullable(klass);
Map propertyIndex = ((Index) classPropertyIndex).getNotNull(klass);
for (Iterator nameMethodIterator = methodIndex.entrySet().iterator(); nameMethodIterator.hasNext();) {
Map.Entry entry = (Map.Entry) nameMethodIterator.next();
String methodName = (String) entry.getKey();
// name too sort?
if (methodName.length() < 4) continue;
//possible getter/setter
boolean isGetter = methodName.startsWith("get");
boolean isSetter = methodName.startsWith("set");
if (!isGetter && !isSetter) continue;
// get the name of the property
String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
MetaMethod propertyMethod = findPropertyMethod((List) entry.getValue(), isGetter);
if (propertyMethod==null) continue;
createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
}
}
}
private void createMetaBeanProperty(Map propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod){
// is this property already accounted for?
MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
if (mp == null) {
if (isGetter) {
mp = new MetaBeanProperty(propName,
propertyMethod.getReturnType(),
propertyMethod, null);
} else {
//isSetter
mp = new MetaBeanProperty(propName,
propertyMethod.getParameterTypes()[0].cachedClass,
null, propertyMethod);
}
} else {
MetaBeanProperty mbp;
MetaFieldProperty mfp;
if (mp instanceof MetaBeanProperty) {
mbp = (MetaBeanProperty) mp;
mfp = mbp.getField();
} else if (mp instanceof MetaFieldProperty){
mfp = (MetaFieldProperty) mp;
mbp = new MetaBeanProperty(propName,
mfp.getType(),
null, null);
} else {
throw new GroovyBugError("unknown MetaProperty class used. Class is "+mp.getClass());
}
// we may have already found one for this name
if (isGetter && mbp.getGetter()==null) {
mbp.setGetter(propertyMethod);
} else if (!isGetter && mbp.getSetter()==null) {
mbp.setSetter(propertyMethod);
}
mbp.setField(mfp);
mp = mbp;
}
propertyIndex.put(propName, mp);
}
private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
// now iterate over the map of property descriptors and generate
// MetaBeanProperty objects
for(int i=0; i<propertyDescriptors.length; i++) {
PropertyDescriptor pd = propertyDescriptors[i];
// skip if the property type is unknown (this seems to be the case if the
// property descriptor is based on a setX() method that has two parameters,
// which is not a valid property)
if(pd.getPropertyType() == null)
continue;
// get the getter method
Method method = pd.getReadMethod();
MetaMethod getter;
if(method != null)
getter = findMethod(method);
else
getter = null;
// get the setter method
MetaMethod setter;
method = pd.getWriteMethod();
if(method != null)
setter = findMethod(method);
else
setter = null;
// now create the MetaProperty object
MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
addMetaBeanProperty(mp);
}
}
/**
* Adds a new MetaBeanProperty to this MetaClass
*
* @param mp The MetaBeanProperty
*/
public void addMetaBeanProperty(MetaBeanProperty mp) {
MetaProperty staticProperty = establishStaticMetaProperty(mp);
if(staticProperty!=null) {
staticPropertyIndex.put(mp.getName(),mp);
}
else {
Map propertyMap = classPropertyIndex.getNotNull(theClass);
//keep field
MetaFieldProperty field;
MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
if (old!=null) {
if (old instanceof MetaBeanProperty) {
field = ((MetaBeanProperty) old).getField();
} else {
field = (MetaFieldProperty) old;
}
mp.setField(field);
}
// put it in the list
// this will overwrite a possible field property
propertyMap.put(mp.getName(), mp);
}
}
/**
* Sets the property value on an object
*/
public void setProperty(Class sender,Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
checkInitalised();
//----------------------------------------------------------------------
// handling of static
//----------------------------------------------------------------------
boolean isStatic = theClass != Class.class && object instanceof Class;
if (isStatic && object != theClass) {
MetaClass mc = registry.getMetaClass((Class) object);
mc.getProperty(sender,object,name,useSuper,fromInsideClass);
return;
}
//----------------------------------------------------------------------
// Unwrap wrapped values fo now - the new MOP will handle them properly
//----------------------------------------------------------------------
if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap();
//----------------------------------------------------------------------
// turn setProperty on a Map to put on the Map itself
//----------------------------------------------------------------------
if(!isStatic && this.isMap) {
((Map)object).put(name, newValue);
return;
}
MetaMethod method = null;
Object[] arguments = null;
//----------------------------------------------------------------------
// setter
//----------------------------------------------------------------------
MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
MetaProperty field = null;
if (mp != null) {
if (mp instanceof MetaBeanProperty) {
MetaBeanProperty mbp = (MetaBeanProperty) mp;
method = mbp.getSetter();
MetaProperty f = mbp.getField();
if (method!=null || (f!=null && !Modifier.isFinal(f.getModifiers()))) {
arguments = new Object[] { newValue };
field = f;
}
} else {
field = mp;
}
}
// check for a category method named like a setter
if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
String getterName = "set"+MetaClassHelper.capitalize(name);
MetaMethod categoryMethod = getCategoryMethodSetter(sender,getterName,false);
if (categoryMethod!=null) {
method = categoryMethod;
arguments = new Object[] { newValue };
}
}
//----------------------------------------------------------------------
// listener method
//----------------------------------------------------------------------
boolean ambigousListener = false;
if (method==null) {
method = (MetaMethod) listeners.get(name);
ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
if ( method != null &&
!ambigousListener &&
newValue instanceof Closure)
{
// lets create a dynamic proxy
Object proxy = Proxy.newProxyInstance(
theClass.getClassLoader(),
new Class[]{method.getParameterTypes()[0].cachedClass},
new ConvertedClosure((Closure) newValue,name));
arguments = new Object[] { proxy };
newValue = proxy;
} else {
method = null;
}
}
//----------------------------------------------------------------------
// field
//----------------------------------------------------------------------
if (method==null && field!=null) {
field.setProperty(object,newValue);
return;
}
//----------------------------------------------------------------------
// generic set method
//----------------------------------------------------------------------
// check for a generic get method provided through a category
if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
method = getCategoryMethodSetter(sender,"set",true);
if (method!=null) arguments = new Object[]{name,newValue};
}
// the generic method is valid, if available (!=null), if static or
// if it is not static and we do no static access
if (method==null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
arguments = new Object[]{ name, newValue };
method = genericSetMethod;
}
//----------------------------------------------------------------------
// executing the getter method
//----------------------------------------------------------------------
if (method!=null) {
if (arguments.length==1) {
newValue = DefaultTypeTransformation.castToType(
newValue,
method.getParameterTypes()[0].cachedClass);
arguments[0] = newValue;
} else {
newValue = DefaultTypeTransformation.castToType(
newValue,
method.getParameterTypes()[1].cachedClass);
arguments[1] = newValue;
}
MetaClassHelper.doMethodInvoke(object,method,arguments);
return;
}
//----------------------------------------------------------------------
// error due to missing method/field
//----------------------------------------------------------------------
if (ambigousListener){
throw new GroovyRuntimeException("There are multiple listeners for the property "+name+". Please do not use the bean short form to access this listener.");
}
if (mp!=null) {
throw new ReadOnlyPropertyException(name,theClass);
}
invokeMissingProperty(object, name, newValue, false);
}
private MetaProperty getMetaProperty(Class clazz, String name, boolean useSuper, boolean useStatic)
{
while (true)
{
Map propertyMap;
if (useStatic)
{
propertyMap = staticPropertyIndex;
}
else if (useSuper)
{
propertyMap = (Map) classPropertyIndexForSuper.getNullable(clazz);
}
else
{
propertyMap = (Map) classPropertyIndex.getNullable(clazz);
}
if (propertyMap == null)
{
if (clazz != theClass)
{
clazz = theClass;
continue;
}
else
{
return null;
}
}
return (MetaProperty) propertyMap.get(name);
}
}
public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
return getAttribute(receiver,messageName);
}
/**
* Looks up the given attribute (field) on the given object
*/
public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
checkInitalised();
boolean isStatic = theClass != Class.class && object instanceof Class;
if (isStatic && object != theClass) {
MetaClass mc = registry.getMetaClass((Class) object);
return mc.getAttribute(sender,object,attribute,useSuper);
}
MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
if (mp != null) {
if (mp instanceof MetaBeanProperty) {
MetaBeanProperty mbp = (MetaBeanProperty) mp;
mp = mbp.getField();
}
try {
// delegate the get operation to the metaproperty
if (mp != null) return mp.getProperty(object);
} catch(Exception e) {
throw new GroovyRuntimeException("Cannot read field: " + attribute,e);
}
}
throw new MissingFieldException(attribute, theClass);
}
/**
* Sets the given attribute (field) on the given object
*/
public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
checkInitalised();
boolean isStatic = theClass != Class.class && object instanceof Class;
if (isStatic && object != theClass) {
MetaClass mc = registry.getMetaClass((Class) object);
mc.setAttribute(sender,object,attribute,newValue,useSuper,fromInsideClass);
return;
}
MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic);
if (mp != null) {
if (mp instanceof MetaBeanProperty) {
MetaBeanProperty mbp = (MetaBeanProperty) mp;
mp = mbp.getField();
}
if (mp != null) {
mp.setProperty(object,newValue);
return;
}
}
throw new MissingFieldException(attribute, theClass);
}
public ClassNode getClassNode() {
if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
// lets try load it from the classpath
String className = theClass.getName();
String groovyFile = className;
int idx = groovyFile.indexOf('$');
if (idx > 0) {
groovyFile = groovyFile.substring(0, idx);
}
groovyFile = groovyFile.replace('.', '/') + ".groovy";
//System.out.println("Attempting to load: " + groovyFile);
URL url = theClass.getClassLoader().getResource(groovyFile);
if (url == null) {
url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
}
if (url != null) {
try {
/**
* todo there is no CompileUnit in scope so class name
* checking won't work but that mostly affects the bytecode
* generation rather than viewing the AST
*/
CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
public void call( ClassVisitor writer, ClassNode node ) {
if( node.getName().equals(theClass.getName()) ) {
MetaClassImpl.this.classNode = node;
}
}
};
final ClassLoader parent = theClass.getClassLoader();
GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new GroovyClassLoader(parent);
}
});
CompilationUnit unit = new CompilationUnit( );
unit.setClassgenCallback( search );
unit.addSource( url );
unit.compile( Phases.CLASS_GENERATION );
}
catch (Exception e) {
throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
}
}
}
return classNode;
}
public String toString() {
return super.toString() + "[" + theClass + "]";
}
// Implementation methods
//-------------------------------------------------------------------------
/**
* Adds all the methods declared in the given class to the metaclass
* ignoring any matching methods already defined by a derived class
*
* @param aClass
*/
private void addMethods(final Class aClass) {
classMethodIndex.getNotNull(aClass);
// add methods directly declared in the class
CachedMethod[] cachedMethods = ReflectionCache.getDeclaredMethodsCached(aClass);
for (int i = 0; i < cachedMethods.length; i++) {
final CachedMethod cachedMethod = cachedMethods[i];
Method reflectionMethod = cachedMethod.cachedMethod;
if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
// Skip Synthetic methods inserted by JDK 1.5 compilers and later
continue;
} /*else if (Modifier.isAbstract(reflectionMethod.getModifiers())) {
continue;
}*/
MetaMethod method = createMetaMethod(reflectionMethod, cachedMethod.getParameterTypes());
addMetaMethod(method);
}
// add methods declared by DGM
List methods = ((MetaClassRegistryImpl)registry).getInstanceMethods();
for (Iterator iter = methods.iterator(); iter.hasNext();) {
CachedMethod element = (CachedMethod) iter.next();
if (element.getParameterTypes()[0].cachedClass !=aClass) continue;
addNewInstanceMethod(element.cachedMethod, element.getParameterTypes());
}
// add static methods declared by DGM
methods = ((MetaClassRegistryImpl)registry).getStaticMethods();
for (Iterator iter = methods.iterator(); iter.hasNext();) {
CachedMethod element = (CachedMethod) iter.next();
if (element.getParameterTypes()[0].cachedClass !=aClass) continue;
addNewStaticMethod(element.cachedMethod, element.getParameterTypes());
}
}
/**
* adds a MetaMethod to this class. WARNING: this method will not
* do the neccessary steps for multimethod logic and using this
* method doesn't mean, that a method added here is replacing another
* method from a parent class completely. These steps are usually done
* by initalize, which means if you need these steps, you have to add
* the method before running initialize the first time.
* @see #initialize()
* @param method the MetaMethod
*/
public void addMetaMethod(MetaMethod method) {
if (isInitialized()) {
throw new RuntimeException("Already initialized, cannot add new method: " + method);
}
if (isGenericGetMethod(method) && genericGetMethod == null) {
genericGetMethod = method;
}
else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
genericSetMethod = method;
}
if (method.isStatic()) {
classStaticMethodIndex.addToClassMethodIndex(method);
}
classMethodIndex.addToClassMethodIndex(method);
}
protected boolean isInitialized(){
return initialized;
}
void addMethodToList(List list, MetaMethod method) {
MetaMethod match = removeMatchingMethod(list,method);
if (match==null) {
list.add(method);
} else if (match.isPrivate()){
// do not overwrite private methods
// Note: private methods from parent classes are not shown here,
// but when doing the multimethod connection step, we overwrite
// methods of the parent class with methods of a subclass and
// in that case we want to keep the private methods
list.add(match);
} else {
Class methodC = method.getDeclaringClass();
Class matchC = match.getDeclaringClass();
if (methodC == matchC){
if (method instanceof NewInstanceMetaMethod) {
// let DGM replace existing methods
list.add(method);
}
else if(method instanceof NewStaticMetaMethod) {
list.add(method);
}
else {
list.add(match);
}
} else if (MetaClassHelper.isAssignableFrom(methodC,matchC)){
list.add(match);
} else {
list.add(method);
}
}
}
/**
* remove a method of the same matching prototype was found in the list
*/
private MetaMethod removeMatchingMethod(List list, MetaMethod method) {
for (Iterator iter = list.iterator(); iter.hasNext();) {
MetaMethod aMethod = (MetaMethod) iter.next();
CachedClass[] params1 = aMethod.getParameterTypes();
CachedClass[] params2 = method.getParameterTypes();
if (params1.length == params2.length) {
boolean matches = true;
for (int i = 0; i < params1.length; i++) {
if (params1[i] != params2[i]) {
matches = false;
break;
}
}
if (matches) {
iter.remove();
return aMethod;
}
}
}
return null;
}
/**
* @return the matching method which should be found
*/
private MetaMethod findMethod(Method aMethod) {
List methods = getMethods(theClass,aMethod.getName(),false);
for (Iterator iter = methods.iterator(); iter.hasNext();) {
MetaMethod method = (MetaMethod) iter.next();
if (method.isMethod(aMethod)) {
return method;
}
}
//log.warning("Creating reflection based dispatcher for: " + aMethod);
return new ReflectionMetaMethod(ReflectionCache.getCachedMethod(aMethod));
}
private static Object doConstructorInvoke(final Class at, CachedConstructor constructor, Object[] argumentArray, boolean setAccessible) {
if (LOG.isLoggable(Level.FINER)) {
MetaClassHelper.logMethodCall(constructor.cachedConstructor.getDeclaringClass(), constructor.cachedConstructor.getName(), argumentArray);
}
// if (setAccessible) {
// // To fix JIRA 435
// // Every constructor should be opened to the accessible classes.
// final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
// final Constructor ctor = constructor;
// AccessController.doPrivileged(new PrivilegedAction() {
// public Object run() {
// ctor.setAccessible(accessible);
// return null;
// }
// });
// }
return MetaClassHelper.doConstructorInvoke(constructor,argumentArray);
}
/**
* Chooses the correct method to use from a list of methods which match by
* name.
*
* @param methods
* the possible methods to choose from
* @param arguments
* the original argument to the method
*/
private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
int methodCount = methods.size();
if (methodCount <= 0) {
return null;
}
else if (methodCount == 1) {
Object method = methods.get(0);
if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
return method;
}
return null;
}
Object answer;
if (arguments == null || arguments.length == 0) {
answer = MetaClassHelper.chooseEmptyMethodParams(methods);
}
else if (arguments.length == 1 && arguments[0] == null) {
answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
}
else {
List matchingMethods = new ArrayList(methods.size());
for (Iterator iter = methods.iterator(); iter.hasNext();) {
Object method = iter.next();
// making this false helps find matches
if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
matchingMethods.add(method);
}
}
if (matchingMethods.isEmpty()) {
return null;
}
else if (matchingMethods.size() == 1) {
return matchingMethods.get(0);
}
return chooseMostSpecificParams(methodName, matchingMethods, arguments);
}
if (answer != null) {
return answer;
}
throw new GroovyRuntimeException(
"Could not find which method to invoke from this list: "
+ methods
+ " for arguments: "
+ InvokerHelper.toString(arguments));
}
private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
long matchesDistance = -1;
LinkedList matches = new LinkedList();
for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
Object method = iter.next();
Class[] paramTypes = MetaClassHelper.getParameterTypes(method).getNativeParameterTypes();
if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
if (dist==0) return method;
if (matches.size()==0) {
matches.add(method);
matchesDistance = dist;
} else if (dist<matchesDistance) {
matchesDistance=dist;
matches.clear();
matches.add(method);
} else if (dist==matchesDistance) {
matches.add(method);
}
}
if (matches.size()==1) {
return matches.getFirst();
}
if (matches.size()==0) {
return null;
}
//more than one matching method found --> ambigous!
String msg = "Ambiguous method overloading for method ";
msg+= theClass.getName()+"#"+name;
msg+= ".\nCannot resolve which method to invoke for ";
msg+= InvokerHelper.toString(arguments);
msg+= " due to overlapping prototypes between:";
for (Iterator iter = matches.iterator(); iter.hasNext();) {
Class[] types=MetaClassHelper.getParameterTypes(iter.next()).getNativeParameterTypes();
msg+= "\n\t"+InvokerHelper.toString(types);
}
throw new GroovyRuntimeException(msg);
}
private boolean isGenericGetMethod(MetaMethod method) {
if (method.getName().equals("get")) {
CachedClass[] parameterTypes = method.getParameterTypes();
return parameterTypes.length == 1 && parameterTypes[0].cachedClass == String.class;
}
return false;
}
public synchronized void initialize() {
if (!isInitialized()) {
fillMethodIndex();
addProperties();
initialized = true;
}
if (reflector == null) {
generateReflector();
}
}
private void addProperties() {
BeanInfo info;
// introspect
try {
info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IntrospectionException {
return Introspector.getBeanInfo(theClass);
}
});
} catch (PrivilegedActionException pae) {
throw new GroovyRuntimeException("exception while bean introspection",pae.getException());
}
PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
// build up the metaproperties based on the public fields, property descriptors,
// and the getters and setters
setupProperties(descriptors);
EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
for (int i = 0; i < eventDescriptors.length; i++) {
EventSetDescriptor descriptor = eventDescriptors[i];
Method[] listenerMethods = descriptor.getListenerMethods();
for (int j = 0; j < listenerMethods.length; j++) {
Method listenerMethod = listenerMethods[j];
MetaMethod metaMethod = createMetaMethod(CachedMethod.find(descriptor.getAddListenerMethod()));
String name = listenerMethod.getName();
if (listeners.containsKey(name)) {
listeners.put(name, AMBIGOUS_LISTENER_METHOD);
} else{
listeners.put(name, metaMethod);
}
}
}
}
private MetaMethod createMetaMethod(final CachedMethod method) {
return createMetaMethod(method.cachedMethod, method.getParameterTypes());
}
private MetaMethod createMetaMethod(final Method method, CachedClass [] pt)
{
if (((MetaClassRegistryImpl)registry).useAccessible()) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
method.setAccessible(true);
return null;
}
});
}
return createMetaMethod0(method, pt);
}
private MetaMethod createMetaMethod0(Method method, CachedClass [] pt)
{
MetaMethod answer = new MetaMethod(method, pt);
if (isValidReflectorMethod(answer)) {
allMethods.add(answer);
answer.setMethodIndex(allMethods.size());
}
else {
//log.warning("Creating reflection based dispatcher for: " + method);
answer = new ReflectionMetaMethod(method, pt);
}
if (GroovySystem.isUseReflection()) {
//log.warning("Creating reflection based dispatcher for: " + method);
return new ReflectionMetaMethod(method, pt);
}
return answer;
}
private boolean isValidReflectorMethod(MetaMethod method) {
// We cannot use a reflector if the method is private, protected, or package accessible only.
if (!method.isPublic()) return false;
if (method.getDeclaringClass().isInterface()) return true;
// lets see if this method is implemented on an interface
List interfaceMethods = getInterfaceMethods();
for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) {
MetaMethod aMethod = (MetaMethod) iter.next();
if (method.isSame(aMethod)) {
method.setInterfaceClass(aMethod.getCallClass());
return true;
}
}
// it's no interface method, so try to find the highest class
// in hierarchy defining this method
CachedClass declaringClass = ReflectionCache.getCachedClass(method.getCallClass());
for (CachedClass clazz=declaringClass; clazz!=null; clazz=clazz.getCachedSuperClass()) {
try {
final String mName = method.getName();
final CachedClass[] parms = method.getParameterTypes();
if (!Modifier.isPublic(clazz.getModifiers())) continue;
Method m = clazz.searchMethods(mName, parms);
if (m == null || !Modifier.isPublic(m.getModifiers())) continue;
declaringClass = clazz;
} catch (SecurityException e) {
continue;
}
}
if (!Modifier.isPublic(declaringClass.getModifiers())) return false;
method.setCallClass(declaringClass.cachedClass);
return true;
}
private void generateReflector() {
if (GroovySystem.isUseReflection()) {
reflector = SKIP_REFLECTOR;
} else {
reflector = ((MetaClassRegistryImpl)registry).loadReflector(theClass, allMethods);
if (reflector == null) {
reflector = SKIP_REFLECTOR;
return;
}
// lets set the reflector on all the methods
for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
MetaMethod metaMethod = (MetaMethod) iter.next();
metaMethod.setReflector(reflector);
}
}
}
public List getMethods() {
return allMethods;
}
public List getMetaMethods() {
return new ArrayList(newGroovyMethodsList);
}
private synchronized List getInterfaceMethods() {
if (interfaceMethods == null) {
interfaceMethods = new ArrayList();
Set interfaces = new HashSet();
makeInterfaceSet(theClass,interfaces);
if (theClass.isInterface()) interfaces.add(theClass);
for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
Class iface = (Class) iter.next();
Method[] methods = iface.getMethods();
addInterfaceMethods(interfaceMethods, methods);
}
}
return interfaceMethods;
}
private void addInterfaceMethods(List list, Method[] methods) {
for (int i = 0; i < methods.length; i++) {
list.add(createMetaMethod(ReflectionCache.getCachedMethod(methods[i])));
}
}
private static class MethodIndexAction {
public void iterate(MethodIndex classMethodIndex){
for (Iterator iter = classMethodIndex.getEntrySetIterator(); iter.hasNext();) {
Map.Entry classEntry = (Map.Entry) iter.next();
Map methodIndex = (Map) classEntry.getValue();
Class clazz = (Class) classEntry.getKey();
if (skipClass(clazz)) continue;
for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
Map.Entry nameEntry = (Map.Entry) iterator.next();
String name = (String) nameEntry.getKey();
List oldList = (List) nameEntry.getValue();
List newList = methodNameAction(clazz, name, oldList);
if (replaceMethodList()) nameEntry.setValue(newList);
}
}
}
public List methodNameAction(Class clazz, String methodName, List methods) {
List newList = new ArrayList(methods.size());
for (Iterator methodIter = methods.iterator(); methodIter.hasNext();) {
MetaMethod method = (MetaMethod) methodIter.next();
methodListAction(clazz,methodName,method,methods,newList);
}
return newList;
}
public boolean skipClass(Class clazz) {return false;}
public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {}
public boolean replaceMethodList(){return true;}
}
public Object getProperty(Object object, String property) {
return getProperty(theClass,object,property,false,false);
}
public void setProperty(Object object, String property, Object newValue) {
setProperty(theClass,object,property,newValue,false,false);
}
public Object getAttribute(Object object, String attribute) {
return getAttribute(theClass,object,attribute,false,false);
}
public void setAttribute(Object object, String attribute, Object newValue) {
setAttribute(theClass,object,attribute,newValue,false,false);
}
public MetaMethod pickMethod(String methodName, Class[] arguments) {
return getMethodWithoutCaching(theClass,methodName,arguments,false);
}
/**
* @deprecated use pickMethod instead
*/
protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
return pickMethod(methodName,arguments);
}
/**
* remove all method call cache entries. This should be done if a
* method is added during runtime, but not by using a category.
*/
protected void clearInvocationCaches() {
staticMethodCache.clear();
methodCache.clear();
}
class MethodIndex extends Index {
public MethodIndex(int size) {
super(size);
}
public MethodIndex() {
super();
}
void addToClassMethodIndex(MetaMethod method) {
final Class declaringClass = method.getDeclaringClass();
Map methodIndex = getNotNull(declaringClass);
String name = method.getName();
List list = (List) methodIndex.get(name);
if (list == null) {
list = new ArrayList();
methodIndex.put(name, list);
list.add(method);
} else {
addMethodToList(list,method);
}
}
MethodIndex copy() {
MethodIndex result = new MethodIndex();
for (Iterator iter = getEntrySetIterator(); iter.hasNext();) {
Map.Entry cmiEntry = (Map.Entry) iter.next();
Map methodIndex = (Map) cmiEntry.getValue();
Map copy = new HashMap(methodIndex.size());
for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
Map.Entry mEntry = (Map.Entry) iterator.next();
copy.put(mEntry.getKey(), new ArrayList((List) mEntry.getValue()));
}
result.put((Class) cmiEntry.getKey(),copy);
}
return result;
}
}
/**
* Created by IntelliJ IDEA.
* User: applerestore
* Date: Sep 11, 2007
* Time: 11:12:07 AM
* To change this template use File | Settings | File Templates.
*/
public static class Index {
protected final HashMap map;
public Index(int size) {
map = new HashMap(size);
}
public Index() {
map = new HashMap();
}
public Map getNotNull(Class key) {
Map res = (Map) map.get(key);
if (res == null) {
res = new HashMap();
map.put(key, res);
}
return res;
}
public void put(Class key, Map value) {
map.put (key, value);
}
public Map getNullable(Class clazz) {
return (Map) map.get(clazz);
}
public Iterator getEntrySetIterator() {
return map.entrySet().iterator ();
}
}
}