Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.lang;
import org.apache.groovy.internal.util.UncheckedThrow;
import org.apache.groovy.util.BeanUtils;
import org.apache.groovy.util.SystemUtil;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.reflection.CacheAccessControlException;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.reflection.CachedConstructor;
import org.codehaus.groovy.reflection.CachedField;
import org.codehaus.groovy.reflection.CachedMethod;
import org.codehaus.groovy.reflection.ClassInfo;
import org.codehaus.groovy.reflection.GeneratedMetaMethod;
import org.codehaus.groovy.reflection.ParameterTypes;
import org.codehaus.groovy.reflection.ReflectionCache;
import org.codehaus.groovy.reflection.android.AndroidSupport;
import org.codehaus.groovy.runtime.ArrayTypeUtils;
import org.codehaus.groovy.runtime.ConvertedClosure;
import org.codehaus.groovy.runtime.CurriedClosure;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.GroovyCategorySupport;
import org.codehaus.groovy.runtime.GroovyCategorySupport.CategoryMethod;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.InvokerInvocationException;
import org.codehaus.groovy.runtime.MetaClassHelper;
import org.codehaus.groovy.runtime.MethodClosure;
import org.codehaus.groovy.runtime.callsite.AbstractCallSite;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.callsite.ConstructorSite;
import org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite;
import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite;
import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite;
import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite;
import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite;
import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite;
import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex;
import org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetBeanMethodMetaProperty;
import org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetMethodMetaProperty;
import org.codehaus.groovy.runtime.metaclass.MethodSelectionException;
import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed;
import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
import org.codehaus.groovy.runtime.metaclass.MultipleSetterProperty;
import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
import org.codehaus.groovy.runtime.metaclass.NewMetaMethod;
import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod;
import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.NumberMathModificationInfo;
import org.codehaus.groovy.runtime.wrappers.Wrapper;
import org.codehaus.groovy.util.ComplexKeyHashMap;
import org.codehaus.groovy.util.FastArray;
import org.codehaus.groovy.util.SingleKeyHashMap;
import org.codehaus.groovy.vmplugin.VMPlugin;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static groovy.lang.Tuple.tuple;
import static java.lang.Character.isUpperCase;
import static org.apache.groovy.util.Arrays.concat;
import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
import static org.codehaus.groovy.reflection.ReflectionCache.isAssignableFrom;
/**
* Allows methods to be dynamically added to existing classes at runtime
*
* @see groovy.lang.MetaClass
*/
public class MetaClassImpl implements MetaClass, MutableMetaClass {
public static final Object[] EMPTY_ARGUMENTS = {};
protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
protected static final String METHOD_MISSING = "methodMissing";
protected static final String PROPERTY_MISSING = "propertyMissing";
protected static final String INVOKE_METHOD_METHOD = "invokeMethod";
private static final String CALL_METHOD = "call";
private static final String DO_CALL_METHOD = "doCall";
private static final String GET_PROPERTY_METHOD = "getProperty";
private static final String SET_PROPERTY_METHOD = "setProperty";
private static final String CONSTRUCTOR_NAME = "";
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 MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod();
private static final Comparator CACHED_CLASS_NAME_COMPARATOR = Comparator.comparing(CachedClass::getName);
private static final boolean PERMISSIVE_PROPERTY_ACCESS = SystemUtil.getBooleanSafe("groovy.permissive.property.access");
private static final VMPlugin VM_PLUGIN = VMPluginFactory.getPlugin();
protected final Class theClass;
protected final CachedClass theCachedClass;
protected final boolean isGroovyObject;
protected final boolean isMap;
protected final MetaMethodIndex metaMethodIndex;
private final Index classPropertyIndex = new MethodIndex();
private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
private final Map listeners = new HashMap<>();
private final List allMethods = new ArrayList<>();
// we only need one of these that can be reused over and over.
private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
private final Index classPropertyIndexForSuper = new MethodIndex();
private final Set newGroovyMethodsSet = new HashSet<>();
private final MetaMethod[] myNewMetaMethods;
private final MetaMethod[] additionalMetaMethods;
protected MetaMethod getPropertyMethod;
protected MetaMethod invokeMethodMethod;
protected MetaMethod setPropertyMethod;
protected MetaClassRegistry registry;
private ClassNode classNode;
private FastArray constructors;
private volatile boolean initialized;
private MetaMethod genericGetMethod;
private MetaMethod genericSetMethod;
private MetaMethod propertyMissingGet;
private MetaMethod propertyMissingSet;
private MetaMethod methodMissing;
private MetaMethodIndex.Header mainClassMethodHeader;
private boolean permissivePropertyAccess = PERMISSIVE_PROPERTY_ACCESS;
/**
* Constructor
*
* @param theClass The class this is the metaclass dor
* @param add The methods for this class
*/
public MetaClassImpl(final Class theClass, final MetaMethod[] add) {
this.theClass = theClass;
theCachedClass = ReflectionCache.getCachedClass(theClass);
this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
this.isMap = Map.class.isAssignableFrom(theClass);
this.registry = GroovySystem.getMetaClassRegistry();
metaMethodIndex = new MetaMethodIndex(theCachedClass);
final MetaMethod[] metaMethods = theCachedClass.getNewMetaMethods();
if (add != null && add.length != 0) {
myNewMetaMethods = concat(metaMethods, add);
additionalMetaMethods = metaMethods;
} else {
myNewMetaMethods = metaMethods;
additionalMetaMethods = MetaMethod.EMPTY_ARRAY;
}
}
/**
* Constructor that sets the methods to null
*
* @param theClass The class this is the metaclass dor
*/
public MetaClassImpl(final Class theClass) {
this(theClass, null);
}
/**
* Constructor with registry
*
* @param registry The metaclass registry for this MetaClass
* @param theClass The class
* @param add The methods
*/
public MetaClassImpl(final MetaClassRegistry registry, final Class theClass, final MetaMethod[] add) {
this(theClass, add);
this.registry = registry;
this.constructors = new FastArray(theCachedClass.getConstructors());
}
/**
* Constructor with registry setting methods to null
*
* @param registry The metaclass registry for this MetaClass
* @param theClass The class
*/
public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
this(registry, theClass, null);
}
/**
* Returns the cached class for this metaclass
*
* @return The cached class.
*/
public final CachedClass getTheCachedClass() {
return theCachedClass;
}
/**
* Returns the registry for this metaclass
*
* @return The resgistry
*/
public MetaClassRegistry getRegistry() {
return registry;
}
/**
* @see MetaObjectProtocol#respondsTo(Object, String, Object[])
*/
@Override
public List respondsTo(final Object obj, final String name, final Object[] argTypes) {
Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
MetaMethod m = getMetaMethod(name, classes);
if (m != null) {
return Collections.singletonList(m);
}
return Collections.emptyList();
}
/**
* @see MetaObjectProtocol#respondsTo(Object, String)
*/
@Override
public List respondsTo(final Object obj, final String name) {
final Object o = getMethods(getTheClass(), name, false);
if (o instanceof FastArray) {
return ((FastArray) o).toList();
}
return Collections.singletonList(o);
}
/**
* @see MetaObjectProtocol#hasProperty(Object, String)
*/
@Override
public MetaProperty hasProperty(final Object obj, final String name) {
return getMetaProperty(name);
}
/**
* @see MetaObjectProtocol#getMetaProperty(String)
*/
@Override
public MetaProperty getMetaProperty(final String name) {
MetaProperty metaProperty = null;
SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
metaProperty = (MetaProperty) propertyMap.get(name);
if (metaProperty == null) {
metaProperty = (MetaProperty) staticPropertyIndex.get(name);
if (metaProperty == null) {
propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
metaProperty = (MetaProperty) propertyMap.get(name);
if (metaProperty == null) {
MetaBeanProperty property = findPropertyInClassHierarchy(name, theCachedClass);
if (property != null) {
onSuperPropertyFoundInHierarchy(property);
metaProperty = property;
}
}
}
}
return metaProperty;
}
/**
* @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
*/
@Override
public MetaMethod getStaticMetaMethod(final String name, final Object[] argTypes) {
Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
return pickStaticMethod(name, classes);
}
/**
* @see MetaObjectProtocol#getMetaMethod(String, Object[])
*/
@Override
public MetaMethod getMetaMethod(final String name, final Object[] argTypes) {
Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
return pickMethod(name, classes);
}
/**
* Returns the class this object this is the metaclass of.
*
* @return The class contained by this metaclass
*/
public Class getTheClass() {
return this.theClass;
}
/**
* Return whether the class represented by this metaclass instance is an instance of the GroovyObject class
*
* @return true if this is a groovy class, false otherwise.
*/
public boolean isGroovyObject() {
return isGroovyObject;
}
/**
* Fills the method index
*/
private void fillMethodIndex() {
mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
LinkedList superClasses = getSuperClasses();
CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
Set interfaces = theCachedClass.getInterfaces();
addInterfaceMethods(interfaces);
populateMethods(superClasses, firstGroovySuper);
inheritInterfaceNewMetaMethods(interfaces);
if (isGroovyObject) {
metaMethodIndex.copyMethodsToSuper();
connectMultimethods(superClasses, firstGroovySuper);
removeMultimethodsOverloadedWithPrivateMethods();
replaceWithMOPCalls(theCachedClass.mopMethods);
}
}
private void populateMethods(final List superClasses, final CachedClass firstGroovySuper) {
MetaMethodIndex.Header header = metaMethodIndex.getHeader(firstGroovySuper.getTheClass());
CachedClass c;
Iterator iter = superClasses.iterator();
while (iter.hasNext()) {
c = iter.next();
CachedMethod[] cachedMethods = c.getMethods();
for (CachedMethod metaMethod : cachedMethods) {
addToAllMethodsIfPublic(metaMethod);
if (!metaMethod.isPrivate() || c == firstGroovySuper)
addMetaMethodToIndex(metaMethod, header);
}
MetaMethod[] cachedMethods1 = getNewMetaMethods(c);
for (final MetaMethod method : cachedMethods1) {
if (!newGroovyMethodsSet.contains(method)) {
newGroovyMethodsSet.add(method);
addMetaMethodToIndex(method, header);
}
}
if (c == firstGroovySuper)
break;
}
MetaMethodIndex.Header last = header;
while (iter.hasNext()) {
c = iter.next();
header = metaMethodIndex.getHeader(c.getTheClass());
if (last != null) {
metaMethodIndex.copyNonPrivateMethods(last, header);
}
last = header;
for (CachedMethod metaMethod : c.getMethods()) {
addToAllMethodsIfPublic(metaMethod);
addMetaMethodToIndex(metaMethod, header);
}
for (final MetaMethod method : getNewMetaMethods(c)) {
if (method.getName().equals(CONSTRUCTOR_NAME) && !method.getDeclaringClass().equals(theCachedClass)) continue;
if (!newGroovyMethodsSet.contains(method)) {
newGroovyMethodsSet.add(method);
addMetaMethodToIndex(method, header);
}
}
}
}
private MetaMethod[] getNewMetaMethods(final CachedClass c) {
if (theCachedClass != c)
return c.getNewMetaMethods();
return myNewMetaMethods;
}
private void addInterfaceMethods(final Set interfaces) {
MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass);
for (CachedClass c : interfaces) {
for (CachedMethod m : c.getMethods()) {
addMetaMethodToIndex(m, header);
}
}
}
protected LinkedList getSuperClasses() {
LinkedList superClasses = new LinkedList<>();
if (theClass.isInterface()) {
superClasses.addFirst(ReflectionCache.OBJECT_CLASS);
} else {
for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
superClasses.addFirst(c);
}
if (theCachedClass.isArray && theClass != Object[].class && !theClass.getComponentType().isPrimitive()) {
superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS);
}
}
return superClasses;
}
private void removeMultimethodsOverloadedWithPrivateMethods() {
MethodIndexAction mia = new MethodIndexAction() {
@Override
public boolean skipClass(final Class> clazz) {
return clazz == theClass;
}
@Override
public void methodNameAction(final Class> clazz, final MetaMethodIndex.Entry e) {
if (e.methods == null)
return;
boolean hasPrivate = false;
if (e.methods instanceof FastArray) {
FastArray methods = (FastArray) e.methods;
final int len = methods.size();
final Object[] data = methods.getArray();
for (int i = 0; i != len; ++i) {
MetaMethod method = (MetaMethod) data[i];
if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
hasPrivate = true;
break;
}
}
} else {
MetaMethod method = (MetaMethod) e.methods;
if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
hasPrivate = true;
}
}
if (!hasPrivate) return;
// We have private methods for that name, so remove the
// multimethods. That is the same as in our index for
// super, so just copy the list from there. It is not
// possible to use a pointer here, because the methods
// in the index for super are replaced later by MOP
// methods like super$5$foo
final Object o = e.methodsForSuper;
if (o instanceof FastArray) {
e.methods = ((FastArray) o).copy();
} else {
e.methods = o;
}
}
};
mia.iterate();
}
private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
// no MOP methods if not a child of GroovyObject
if (!isGroovyObject) return;
class MOPIter extends MethodIndexAction {
boolean useThis;
@Override
public void methodNameAction(final Class> clazz, final MetaMethodIndex.Entry e) {
if (useThis) {
if (e.methods == null)
return;
if (e.methods instanceof FastArray) {
FastArray methods = (FastArray) e.methods;
processFastArray(methods);
} else {
MetaMethod method = (MetaMethod) e.methods;
if (method instanceof NewMetaMethod)
return;
if (useThis ^ Modifier.isPrivate(method.getModifiers())) return;
String mopName = method.getMopName();
int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
if (index >= 0) {
int matchingMethod = findMatchingMethod(method, mopName, index, mopMethods);
if (matchingMethod != -1) {
e.methods = mopMethods[matchingMethod];
}
}
}
} else {
if (e.methodsForSuper == null)
return;
if (e.methodsForSuper instanceof FastArray) {
FastArray methods = (FastArray) e.methodsForSuper;
processFastArray(methods);
} else {
MetaMethod method = (MetaMethod) e.methodsForSuper;
if (method instanceof NewMetaMethod)
return;
if (useThis ^ Modifier.isPrivate(method.getModifiers())) return;
String mopName = method.getMopName();
// GROOVY-4922: Due to a numbering scheme change, we must find the super$X$method which exists
// with the highest number. If we don't, no method may be found, leading to a stack overflow
String[] decomposedMopName = decomposeMopName(mopName);
int distance = Integer.parseInt(decomposedMopName[1]);
while (distance > 0) {
String fixedMopName = decomposedMopName[0] + distance + decomposedMopName[2];
int index = Arrays.binarySearch(mopMethods, fixedMopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
if (index >= 0) {
int matchingMethod = findMatchingMethod(method, fixedMopName, index, mopMethods);
if (matchingMethod != -1) {
e.methodsForSuper = mopMethods[matchingMethod];
distance = 0;
}
}
distance -= 1;
}
}
}
}
private String[] decomposeMopName(final String mopName) {
int idx = mopName.indexOf('$');
if (idx > 0) {
int eidx = mopName.indexOf('$', idx + 1);
if (eidx > 0) {
return new String[]{
mopName.substring(0, idx + 1),
mopName.substring(idx + 1, eidx),
mopName.substring(eidx)
};
}
}
return new String[]{"", "0", mopName};
}
private void processFastArray(final FastArray methods) {
final int len = methods.size();
final Object[] data = methods.getArray();
for (int i = 0; i != len; i += 1) {
MetaMethod method = (MetaMethod) data[i];
if (method instanceof NewMetaMethod) continue;
boolean isPrivate = Modifier.isPrivate(method.getModifiers());
if (useThis ^ isPrivate) continue;
String mopName = method.getMopName();
int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
if (index >= 0) {
int matchingMethod = findMatchingMethod(method, mopName, index, mopMethods);
if (matchingMethod != -1) {
methods.set(i, mopMethods[matchingMethod]);
}
}
}
}
}
MOPIter iter = new MOPIter();
// replace all calls for super with the correct MOP method
iter.useThis = false;
iter.iterate();
// replace all calls for this with the correct MOP method
iter.useThis = true;
iter.iterate();
}
private int findMatchingMethod(final MetaMethod method, final String mopName, final int index, final CachedMethod[] mopMethods) {
int from = index;
while (from > 0 && mopMethods[from - 1].getName().equals(mopName))
from -= 1;
int to = index;
while (to < mopMethods.length - 1 && mopMethods[to + 1].getName().equals(mopName))
to += 1;
return findMatchingMethod(mopMethods, from, to, method);
}
private void inheritInterfaceNewMetaMethods(final Set interfaces) {
// add methods declared by DGM for interfaces
for (CachedClass face : interfaces) {
for (MetaMethod method : getNewMetaMethods(face)) {
boolean skip = false;
// skip DGM methods on an interface if the class already has the method
// but don't skip for GroovyObject-related methods as it breaks things :-(
if (method instanceof GeneratedMetaMethod && !isAssignableFrom(GroovyObject.class, method.getDeclaringClass().getTheClass())) {
for (Method m : theClass.getMethods()) {
if (method.getName().equals(m.getName())
// below not true for DGM#push and also co-variant return scenarios
//&& method.getReturnType().equals(m.getReturnType())
&& MetaMethod.equal(method.getParameterTypes(), m.getParameterTypes())) {
skip = true;
break;
}
}
}
if (!skip) {
newGroovyMethodsSet.add(method);
addMetaMethodToIndex(method, mainClassMethodHeader);
}
}
}
}
private void connectMultimethods(final List superClasses, final CachedClass firstGroovyClass) {
MetaMethodIndex.Header last = null;
for (ListIterator iter = superClasses.listIterator(superClasses.size()); iter.hasPrevious(); ) {
CachedClass c = iter.previous();
MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getTheClass());
// We don't copy DGM methods to superclasses' indexes
// The reason we can do that is particular set of DGM methods in use,
// if at some point we will define DGM method for some Groovy class or
// for a class derived from such, we will need to revise this condition.
// It saves us a lot of space and some noticeable time
if (last != null) metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex);
last = methodIndex;
if (c == firstGroovyClass)
break;
}
}
private CachedClass calcFirstGroovySuperClass(final List superClasses) {
if (theCachedClass.isInterface)
return ReflectionCache.OBJECT_CLASS;
CachedClass firstGroovy = null;
Iterator iter = superClasses.iterator();
while (iter.hasNext()) {
CachedClass c = iter.next();
if (GroovyObject.class.isAssignableFrom(c.getTheClass())) {
firstGroovy = c;
break;
}
}
if (firstGroovy == null) {
firstGroovy = theCachedClass;
} else if (firstGroovy.getTheClass() == GroovyObjectSupport.class && iter.hasNext()) {
firstGroovy = iter.next();
if (firstGroovy.getTheClass() == Closure.class && iter.hasNext()) {
firstGroovy = iter.next();
}
}
return GroovyObject.class.isAssignableFrom(firstGroovy.getTheClass()) ? firstGroovy.getCachedSuperClass() : firstGroovy;
}
/**
* Gets all instance methods available on this class for the given name
*
* @return all the normal instance methods available on this class for the
* given name
*/
private Object getMethods(final Class> sender, final String name, final boolean isCallToSuper) {
Object answer;
final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
if (entry == null) {
answer = FastArray.EMPTY_LIST;
} else if (isCallToSuper) {
answer = entry.methodsForSuper;
} else {
answer = entry.methods;
}
if (answer == null) answer = FastArray.EMPTY_LIST;
if (!isCallToSuper) {
List used = GroovyCategorySupport.getCategoryMethods(name);
if (used != null) {
FastArray arr;
if (answer instanceof MetaMethod) {
arr = new FastArray();
arr.add(answer);
} else {
arr = ((FastArray) answer).copy();
}
for (CategoryMethod cm : used) {
if (!cm.getDeclaringClass().getTheClass().isAssignableFrom(sender))
continue;
filterMatchingMethodForCategory(arr, cm);
}
answer = arr;
}
}
return answer;
}
/**
* Returns all the normal static methods on this class for the given name
*
* @return all the normal static methods available on this class for the
* given name
*/
private Object getStaticMethods(final Class> sender, final String name) {
final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
if (entry == null)
return FastArray.EMPTY_LIST;
Object answer = entry.staticMethods;
if (answer == null)
return FastArray.EMPTY_LIST;
return answer;
}
/**
* Returns whether this MetaClassImpl has been modified. Since MetaClassImpl
* is not designed for modification this method always returns false
*
* @return false
*/
@Override
public boolean isModified() {
return false; // MetaClassImpl not designed for modification, just return false
}
/**
* Adds an instance method to this metaclass.
*
* @param method The method to be added
*/
@Override
public void addNewInstanceMethod(final Method method) {
CachedMethod cachedMethod = CachedMethod.find(method);
NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod);
addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(newMethod.getDeclaringClass().getTheClass()));
}
private void addNewInstanceMethodToIndex(final MetaMethod newMethod, final MetaMethodIndex.Header header) {
if (!newGroovyMethodsSet.contains(newMethod)) {
newGroovyMethodsSet.add(newMethod);
addMetaMethodToIndex(newMethod, header);
}
}
/**
* Adds a static method to this metaclass.
*
* @param method The method to be added
*/
@Override
public void addNewStaticMethod(final Method method) {
CachedMethod cachedMethod = CachedMethod.find(method);
NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(newMethod.getDeclaringClass().getTheClass()));
}
private void addNewStaticMethodToIndex(final MetaMethod newMethod, final MetaMethodIndex.Header header) {
if (!newGroovyMethodsSet.contains(newMethod)) {
newGroovyMethodsSet.add(newMethod);
addMetaMethodToIndex(newMethod, header);
}
}
/**
* Invoke a method on the given object with the given arguments.
*
* @param object The object the method should be invoked on.
* @param methodName The name of the method to invoke.
* @param arguments The arguments to the invoked method as null, a Tuple, an array or a single argument of any type.
* @return The result of the method invocation.
*/
@Override
public Object invokeMethod(final Object object, final String methodName, final Object arguments) {
if (arguments == null) {
return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
}
if (arguments instanceof Tuple) {
return invokeMethod(object, methodName, ((Tuple>) arguments).toArray());
}
if (arguments instanceof Object[]) {
return invokeMethod(object, methodName, (Object[]) arguments);
}
return invokeMethod(object, methodName, new Object[]{arguments});
}
/**
* Invoke a missing method on the given object with the given arguments.
*
* @param instance The object the method should be invoked on.
* @param methodName The name of the method to invoke.
* @param arguments The arguments to the invoked method.
* @return The result of the method invocation.
*/
@Override
public Object invokeMissingMethod(final Object instance, final String methodName, final Object[] arguments) {
return invokeMissingMethod(instance, methodName, arguments, null, false);
}
/**
* Invoke a missing property on the given object with the given arguments.
*
* @param instance The object the method should be invoked on.
* @param propertyName The name of the property to invoke.
* @param optionalValue The (optional) new value for the property
* @param isGetter Whether the method is a getter
* @return The result of the method invocation.
*/
@Override
public Object invokeMissingProperty(final Object instance, final String propertyName, final Object optionalValue, final boolean isGetter) {
MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, theCachedClass);
if (property != null) {
onSuperPropertyFoundInHierarchy(property);
if (!isGetter) {
property.setProperty(instance, optionalValue);
return null;
}
return property.getProperty(instance);
}
// look for getProperty or setProperty overrides
if (isGetter) {
final Class>[] getPropertyArgs = {String.class};
final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD, getPropertyArgs, this);
if (method instanceof ClosureMetaMethod) {
onGetPropertyFoundInHierarchy(method);
return method.invoke(instance, new Object[]{propertyName});
}
} else {
final Class>[] setPropertyArgs = {String.class, Object.class};
final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD, setPropertyArgs, this);
if (method instanceof ClosureMetaMethod) {
onSetPropertyFoundInHierarchy(method);
return method.invoke(instance, new Object[]{propertyName, optionalValue});
}
}
try {
if (!(instance instanceof Class)) {
if (isGetter) {
if (propertyMissingGet != null) {
return propertyMissingGet.invoke(instance, new Object[]{propertyName});
}
} else {
if (propertyMissingSet != null) {
return propertyMissingSet.invoke(instance, new Object[]{propertyName, optionalValue});
}
}
}
} catch (InvokerInvocationException iie) {
boolean shouldHandle = isGetter && propertyMissingGet != null;
if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet != null;
if (shouldHandle && iie.getCause() instanceof MissingPropertyException) {
throw (MissingPropertyException) iie.getCause();
}
throw iie;
}
Class> theClass = instance instanceof Class ? (Class>) instance : instance.getClass();
if (instance instanceof Class && theClass != Class.class) {
final MetaProperty metaProperty = InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName);
if (metaProperty != null) {
if (isGetter) {
return metaProperty.getProperty(instance);
}
metaProperty.setProperty(instance, optionalValue);
return null;
}
}
throw new MissingPropertyExceptionNoStack(propertyName, theClass);
}
private Object invokeMissingMethod(final Object instance, final String methodName, final Object[] arguments, final RuntimeException original, final boolean isCallToSuper) {
if (isCallToSuper) {
MetaClass metaClass = InvokerHelper.getMetaClass(theClass.getSuperclass());
return metaClass.invokeMissingMethod(instance, methodName, arguments);
}
Class> instanceKlazz = instance.getClass();
if (theClass != instanceKlazz && theClass.isAssignableFrom(instanceKlazz))
instanceKlazz = theClass;
Class>[] argClasses = MetaClassHelper.castArgumentsToClassArray(arguments);
MetaMethod method = findMixinMethod(methodName, argClasses);
if (method != null) {
onMixinMethodFound(method);
return method.invoke(instance, arguments);
}
method = findMethodInClassHierarchy(instanceKlazz, methodName, argClasses, this);
if (method != null) {
onSuperMethodFoundInHierarchy(method);
return method.invoke(instance, arguments);
}
// still not method here, so see if there is an invokeMethod method up the hierarchy
final Class>[] invokeMethodArgs = {String.class, Object[].class};
method = findMethodInClassHierarchy(instanceKlazz, INVOKE_METHOD_METHOD, invokeMethodArgs, this);
if (method instanceof ClosureMetaMethod) {
onInvokeMethodFoundInHierarchy(method);
return method.invoke(instance, invokeMethodArgs);
}
// last resort look in the category
if (method == null && GroovyCategorySupport.hasCategoryInCurrentThread()) {
method = getCategoryMethodMissing(instanceKlazz);
if (method != null) {
return method.invoke(instance, new Object[]{methodName, arguments});
}
}
if (methodMissing != null) {
try {
return methodMissing.invoke(instance, new Object[]{methodName, arguments});
} catch (InvokerInvocationException iie) {
if (methodMissing instanceof ClosureMetaMethod && iie.getCause() instanceof MissingMethodException) {
MissingMethodException mme = (MissingMethodException) iie.getCause();
throw new MissingMethodExecutionFailed(mme.getMethod(), mme.getClass(),
mme.getArguments(), mme.isStatic(), mme);
}
throw iie;
} catch (MissingMethodException mme) {
if (methodMissing instanceof ClosureMetaMethod) {
throw new MissingMethodExecutionFailed(mme.getMethod(), mme.getClass(),
mme.getArguments(), mme.isStatic(), mme);
} else {
throw mme;
}
}
} else if (original != null) {
throw original;
} else {
throw new MissingMethodExceptionNoStack(methodName, theClass, arguments, false);
}
}
protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) {
}
protected void onMixinMethodFound(MetaMethod method) {
}
protected void onSuperMethodFoundInHierarchy(MetaMethod method) {
}
protected void onInvokeMethodFoundInHierarchy(MetaMethod method) {
}
protected void onSetPropertyFoundInHierarchy(MetaMethod method) {
}
protected void onGetPropertyFoundInHierarchy(MetaMethod method) {
}
/**
* Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up
* "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException
*
* @param instance The instance
* @param propertyName The name of the property
* @param optionalValue The value in the case of a setter
* @param isGetter True if its a getter
* @return The value in the case of a getter or a MissingPropertyException
*/
protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
if (isGetter) {
MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
if (propertyMissing != null) {
return propertyMissing.invoke(instance, new Object[]{propertyName});
}
} else {
MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
if (propertyMissing != null) {
return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
}
}
if (instance instanceof Class) {
throw new MissingPropertyException(propertyName, (Class) instance);
}
throw new MissingPropertyException(propertyName, theClass);
}
/**
* Invokes a method on the given receiver for the specified arguments.
* The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
*
* @param object The object which the method was invoked on
* @param methodName The name of the method
* @param originalArguments The arguments to the method
* @return The return value of the method
* @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean)
*/
public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
return invokeMethod(theClass, object, methodName, originalArguments, false, false);
}
private Object invokeMethodClosure(Object object, Object[] arguments) {
final MethodClosure mc = (MethodClosure) object;
final Object owner = mc.getOwner();
String methodName = mc.getMethod();
final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
// To conform to "Least Surprise" principle, try to invoke method with original arguments first, which can match most of use cases
try {
return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, arguments, false, false);
} catch (MissingMethodExceptionNoStack | InvokerInvocationException e) {
// CONSTRUCTOR REFERENCE
if (owner instanceof Class && MethodClosure.NEW.equals(methodName)) {
if (ownerClass.isArray()) {
if (0 == arguments.length) {
throw new GroovyRuntimeException("The arguments(specifying size) are required to create array[" + ownerClass.getCanonicalName() + "]");
}
int arrayDimension = ArrayTypeUtils.dimension(ownerClass);
if (arguments.length > arrayDimension) {
throw new GroovyRuntimeException("The length[" + arguments.length + "] of arguments should not be greater than the dimensions[" + arrayDimension + "] of array[" + ownerClass.getCanonicalName() + "]");
}
int[] sizeArray = new int[arguments.length];
for (int i = 0, n = sizeArray.length; i < n; i++) {
Object argument = arguments[i];
if (argument instanceof Integer) {
sizeArray[i] = (Integer) argument;
} else {
sizeArray[i] = Integer.parseInt(String.valueOf(argument));
}
}
Class arrayType =
arguments.length == arrayDimension
? ArrayTypeUtils.elementType(ownerClass) // Just for better performance, though we can use reduceDimension only
: ArrayTypeUtils.elementType(ownerClass, (arrayDimension - arguments.length));
return Array.newInstance(arrayType, sizeArray);
}
return ownerMetaClass.invokeConstructor(arguments);
}
// METHOD REFERENCE
// if and only if the owner is a class and the method closure can be related to some instance methods,
// try to invoke method with adjusted arguments(first argument is the actual owner) again.
// otherwise throw the MissingMethodExceptionNoStack.
if (!(owner instanceof Class
&& (Boolean) mc.getProperty(MethodClosure.ANY_INSTANCE_METHOD_EXISTS))) {
throw e;
}
if (arguments.length < 1 || !ownerClass.isAssignableFrom(arguments[0].getClass())) {
return invokeMissingMethod(object, methodName, arguments);
}
Object newReceiver = arguments[0];
Object[] newArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
return ownerMetaClass.invokeMethod(ownerClass, newReceiver, methodName, newArguments, false, false);
}
}
/**
*
Invokes a method on the given receiver for the specified arguments. The sender is the class that invoked the method on the object.
* The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
*
*
The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
* to the super class if necessary
*
* @param sender The java.lang.Class instance that invoked the method
* @param object The object which the method was invoked on
* @param methodName The name of the method
* @param originalArguments The arguments to the method
* @param isCallToSuper Whether the method is a call to a super class method
* @param fromInsideClass Whether the call was invoked from the inside or the outside of the class
* @return The return value of the method
* @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean)
*/
public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
checkInitalised();
if (object == null) {
throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
}
final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
// final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
//
// unwrap(arguments);
MetaMethod method = getMetaMethod(sender, object, methodName, isCallToSuper, arguments);
final boolean isClosure = object instanceof Closure;
if (isClosure) {
final Closure closure = (Closure) object;
final Object owner = closure.getOwner();
if (CALL_METHOD.equals(methodName) || DO_CALL_METHOD.equals(methodName)) {
final Class objectClass = object.getClass();
if (objectClass == MethodClosure.class) {
return this.invokeMethodClosure(object, arguments);
} else if (objectClass == CurriedClosure.class) {
final CurriedClosure cc = (CurriedClosure) object;
// change the arguments for an uncurried call
final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}
if (method == null) invokeMissingMethod(object, methodName, arguments);
}
final Object delegate = closure.getDelegate();
final boolean isClosureNotOwner = owner != closure;
final int resolveStrategy = closure.getResolveStrategy();
final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
switch (resolveStrategy) {
case Closure.TO_SELF:
method = closure.getMetaClass().pickMethod(methodName, argClasses);
if (method != null) return method.invoke(closure, arguments);
break;
case Closure.DELEGATE_ONLY:
if (method == null && delegate != closure && delegate != null) {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
method = delegateMetaClass.pickMethod(methodName, argClasses);
if (method != null) {
return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
} else if (delegate != closure && (delegate instanceof GroovyObject)) {
return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
}
}
break;
case Closure.OWNER_ONLY:
if (method == null && owner != closure) {
MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
}
break;
case Closure.DELEGATE_FIRST:
Tuple2