0001: /*
0002: $Id: MetaClassImpl.java 4669 2007-01-02 19:35:47Z blackdrag $
0003:
0004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
0005:
0006: Redistribution and use of this software and associated documentation
0007: ("Software"), with or without modification, are permitted provided
0008: that the following conditions are met:
0009:
0010: 1. Redistributions of source code must retain copyright
0011: statements and notices. Redistributions must also contain a
0012: copy of this document.
0013:
0014: 2. Redistributions in binary form must reproduce the
0015: above copyright notice, this list of conditions and the
0016: following disclaimer in the documentation and/or other
0017: materials provided with the distribution.
0018:
0019: 3. The name "groovy" must not be used to endorse or promote
0020: products derived from this Software without prior written
0021: permission of The Codehaus. For written permission,
0022: please contact info@codehaus.org.
0023:
0024: 4. Products derived from this Software may not be called "groovy"
0025: nor may "groovy" appear in their names without prior written
0026: permission of The Codehaus. "groovy" is a registered
0027: trademark of The Codehaus.
0028:
0029: 5. Due credit should be given to The Codehaus -
0030: http://groovy.codehaus.org/
0031:
0032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
0033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
0034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
0036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
0037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
0043: OF THE POSSIBILITY OF SUCH DAMAGE.
0044:
0045: */
0046: package groovy.lang;
0047:
0048: import java.beans.BeanInfo;
0049: import java.beans.EventSetDescriptor;
0050: import java.beans.IntrospectionException;
0051: import java.beans.Introspector;
0052: import java.beans.PropertyDescriptor;
0053: import java.lang.reflect.Constructor;
0054: import java.lang.reflect.Field;
0055: import java.lang.reflect.Method;
0056: import java.lang.reflect.Modifier;
0057: import java.net.URL;
0058: import java.security.AccessController;
0059: import java.security.PrivilegedAction;
0060: import java.security.PrivilegedActionException;
0061: import java.security.PrivilegedExceptionAction;
0062: import java.util.ArrayList;
0063: import java.util.Arrays;
0064: import java.util.Collection;
0065: import java.util.Collections;
0066: import java.util.Comparator;
0067: import java.util.HashMap;
0068: import java.util.HashSet;
0069: import java.util.Iterator;
0070: import java.util.LinkedList;
0071: import java.util.List;
0072: import java.util.Map;
0073: import java.util.Set;
0074: import java.util.logging.Level;
0075:
0076: import org.codehaus.groovy.GroovyBugError;
0077: import org.codehaus.groovy.ast.ClassNode;
0078: import org.codehaus.groovy.classgen.BytecodeHelper;
0079: import org.codehaus.groovy.control.CompilationUnit;
0080: import org.codehaus.groovy.control.Phases;
0081: import org.codehaus.groovy.runtime.CurriedClosure;
0082: import org.codehaus.groovy.runtime.DefaultGroovyMethods;
0083: import org.codehaus.groovy.runtime.DefaultMethodKey;
0084: import org.codehaus.groovy.runtime.GroovyCategorySupport;
0085: import org.codehaus.groovy.runtime.InvokerHelper;
0086: import org.codehaus.groovy.runtime.MetaClassHelper;
0087: import org.codehaus.groovy.runtime.MethodClosure;
0088: import org.codehaus.groovy.runtime.MethodKey;
0089: import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
0090: import org.codehaus.groovy.runtime.NewStaticMetaMethod;
0091: import org.codehaus.groovy.runtime.ReflectionMetaMethod;
0092: import org.codehaus.groovy.runtime.Reflector;
0093: import org.codehaus.groovy.runtime.TransformMetaMethod;
0094: import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
0095: import org.codehaus.groovy.runtime.wrappers.Wrapper;
0096: import org.objectweb.asm.ClassVisitor;
0097:
0098: /**
0099: * Allows methods to be dynamically added to existing classes at runtime
0100: *
0101: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
0102: * @author Guillaume Laforge
0103: * @author Jochen Theodorou
0104: * @version $Revision: 4669 $
0105: * @see groovy.lang.MetaClass
0106: */
0107: public class MetaClassImpl extends MetaClass {
0108:
0109: protected MetaClassRegistry registry;
0110: private ClassNode classNode;
0111: private Map classMethodIndex = new HashMap();
0112: private Map classMethodIndexForSuper;
0113: private Map classStaticMethodIndex = new HashMap();
0114: private Map classPropertyIndex = new HashMap();
0115: private Map classPropertyIndexForSuper = new HashMap();
0116: private Map staticPropertyIndex = new HashMap();
0117: private Map listeners = new HashMap();
0118: private Map methodCache = Collections
0119: .synchronizedMap(new HashMap());
0120: private Map staticMethodCache = Collections
0121: .synchronizedMap(new HashMap());
0122: private MetaMethod genericGetMethod;
0123: private MetaMethod genericSetMethod;
0124: private List constructors;
0125: private List allMethods = new ArrayList();
0126: private List interfaceMethods;
0127: private Reflector reflector;
0128: private boolean initialized;
0129: // we only need one of these that can be reused over and over.
0130: private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
0131: private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(
0132: null, null, new Class[] {}, null, 0);
0133: private static final Object[] EMPTY_ARGUMENTS = {};
0134: private List newGroovyMethodsList = new LinkedList();
0135:
0136: public MetaClassImpl(MetaClassRegistry registry,
0137: final Class theClass) {
0138: super (theClass);
0139: this .registry = registry;
0140:
0141: constructors = (List) AccessController
0142: .doPrivileged(new PrivilegedAction() {
0143: public Object run() {
0144: return Arrays.asList(theClass
0145: .getDeclaredConstructors());
0146: }
0147: });
0148: }
0149:
0150: private void fillMethodIndex() {
0151: LinkedList super Classes = getSuperClasses();
0152: // let's add all the base class methods
0153: for (Iterator iter = super Classes.iterator(); iter.hasNext();) {
0154: Class c = (Class) iter.next();
0155: addMethods(c);
0156: }
0157:
0158: Set interfaces = new HashSet();
0159: makeInterfaceSet(theClass, interfaces);
0160:
0161: inheritMethods(super Classes, classMethodIndex);
0162: inheritInterfaceMethods(interfaces);
0163: copyClassMethodIndexForSuper();
0164:
0165: connectMultimethods(super Classes);
0166: populateInterfaces(interfaces);
0167: removeMultimethodsOverloadedWithPrivateMethods();
0168:
0169: replaceWithMOPCalls();
0170: }
0171:
0172: private LinkedList getSuperClasses() {
0173: LinkedList super Classes = new LinkedList();
0174: for (Class c = theClass; c != null; c = c.getSuperclass()) {
0175: super Classes.addFirst(c);
0176: }
0177: if (theClass.isArray() && theClass != Object[].class
0178: && !theClass.getComponentType().isPrimitive()) {
0179: super Classes.addFirst(Object[].class);
0180: }
0181: return super Classes;
0182: }
0183:
0184: private void removeMultimethodsOverloadedWithPrivateMethods() {
0185: Map privates = new HashMap();
0186: MethodIndexAction mia = new MethodIndexAction() {
0187: public List methodNameAction(Class clazz,
0188: String methodName, List methods) {
0189: boolean hasPrivate = false;
0190: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0191: MetaMethod method = (MetaMethod) iter.next();
0192: if (method.isPrivate()
0193: && clazz == method.getDeclaringClass()) {
0194: hasPrivate = true;
0195: break;
0196: }
0197: }
0198: if (!hasPrivate)
0199: return null;
0200: // We have private methods for that name, so remove the
0201: // multimethods. That is the same as in our index for
0202: // super, so just copy the list from there. It is not
0203: // possible to use a pointer here, because the methods
0204: // in the index for super are replaced later by MOP
0205: // methods like super$5$foo
0206: methods.clear();
0207: methods
0208: .addAll((Collection) ((Map) classMethodIndexForSuper
0209: .get(clazz)).get(methodName));
0210: return methods;
0211: }
0212:
0213: public boolean replaceMethodList() {
0214: return false;
0215: }
0216: };
0217: mia.iterate(classMethodIndex);
0218: }
0219:
0220: private void replaceWithMOPCalls() {
0221: // no MOP methods if not a child of GroovyObject
0222: if (!GroovyObject.class.isAssignableFrom(theClass))
0223: return;
0224:
0225: final Map mainClassMethodIndex = (Map) classMethodIndex
0226: .get(theClass);
0227: class MOPIter extends MethodIndexAction {
0228: boolean useThis;
0229:
0230: public boolean skipClass(Class clazz) {
0231: return !useThis && clazz == theClass;
0232: }
0233:
0234: public void methodListAction(Class clazz,
0235: String methodName, MetaMethod method, List oldList,
0236: List newList) {
0237: String mopName = getMOPMethodName(method
0238: .getDeclaringClass(), methodName, useThis);
0239: List matches = (List) mainClassMethodIndex.get(mopName);
0240: if (matches == null) {
0241: newList.add(method);
0242: return;
0243: }
0244: matches = new ArrayList(matches);
0245: MetaMethod matchingMethod = removeMatchingMethod(
0246: matches, method);
0247: if (matchingMethod == null) {
0248: newList.add(method);
0249: return;
0250: } else {
0251: newList.add(matchingMethod);
0252: }
0253: }
0254: }
0255: MOPIter iter = new MOPIter();
0256:
0257: // replace all calls for super with the correct MOP method
0258: iter.useThis = false;
0259: iter.iterate(classMethodIndexForSuper);
0260: // replace all calls for this with the correct MOP method
0261: iter.useThis = true;
0262: iter.iterate(classMethodIndex);
0263: }
0264:
0265: private String getMOPMethodName(Class declaringClass, String name,
0266: boolean useThis) {
0267: int distance = 0;
0268: for (; declaringClass != null; declaringClass = declaringClass
0269: .getSuperclass()) {
0270: distance++;
0271: }
0272: return (useThis ? "this" : "super") + "$" + distance + "$"
0273: + name;
0274: }
0275:
0276: private void copyClassMethodIndexForSuper() {
0277: classMethodIndexForSuper = new HashMap(classMethodIndex.size());
0278: for (Iterator iter = classMethodIndex.entrySet().iterator(); iter
0279: .hasNext();) {
0280: Map.Entry cmiEntry = (Map.Entry) iter.next();
0281: Map methodIndex = (Map) cmiEntry.getValue();
0282: Map copy = new HashMap(methodIndex.size());
0283: for (Iterator iterator = methodIndex.entrySet().iterator(); iterator
0284: .hasNext();) {
0285: Map.Entry mEntry = (Map.Entry) iterator.next();
0286: copy.put(mEntry.getKey(), new ArrayList((List) mEntry
0287: .getValue()));
0288: }
0289: classMethodIndexForSuper.put(cmiEntry.getKey(), copy);
0290: }
0291: }
0292:
0293: private void inheritInterfaceMethods(Set interfaces) {
0294: // add methods declared by DGM for interfaces
0295: List methods = registry.getInstanceMethods();
0296: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0297: Method element = (Method) iter.next();
0298: Class dgmClass = element.getParameterTypes()[0];
0299: if (!interfaces.contains(dgmClass))
0300: continue;
0301: NewInstanceMetaMethod method = new NewInstanceMetaMethod(
0302: createMetaMethod(element));
0303: if (!newGroovyMethodsList.contains(method)) {
0304: newGroovyMethodsList.add(method);
0305: }
0306: Map methodIndex = (Map) classMethodIndex.get(theClass);
0307: List list = (List) methodIndex.get(method.getName());
0308: if (list == null) {
0309: list = new ArrayList();
0310: methodIndex.put(method.getName(), list);
0311: list.add(method);
0312: } else {
0313: addMethodToList(list, method);
0314: }
0315: }
0316: methods = registry.getStaticMethods();
0317: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0318: Method element = (Method) iter.next();
0319: Class dgmClass = element.getParameterTypes()[0];
0320: if (!interfaces.contains(dgmClass))
0321: continue;
0322: addNewStaticMethod(element);
0323: }
0324: }
0325:
0326: private void populateInterfaces(Set interfaces) {
0327: Map currentIndex = (Map) classMethodIndex.get(theClass);
0328: Map index = new HashMap();
0329: copyNonPrivateMethods(currentIndex, index);
0330: for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
0331: Class iClass = (Class) iter.next();
0332: Map methodIndex = (Map) classMethodIndex.get(iClass);
0333: if (methodIndex == null || methodIndex.size() == 0) {
0334: classMethodIndex.put(iClass, index);
0335: continue;
0336: }
0337: copyNonPrivateMethods(currentIndex, methodIndex);
0338: }
0339: }
0340:
0341: private static void makeInterfaceSet(Class c, Set s) {
0342: if (c == null)
0343: return;
0344: Class[] interfaces = c.getInterfaces();
0345: for (int i = 0; i < interfaces.length; i++) {
0346: if (!s.contains(interfaces[i])) {
0347: s.add(interfaces[i]);
0348: makeInterfaceSet(interfaces[i], s);
0349: }
0350: }
0351: makeInterfaceSet(c.getSuperclass(), s);
0352: }
0353:
0354: private void copyNonPrivateMethods(Map from, Map to) {
0355: for (Iterator iterator = from.entrySet().iterator(); iterator
0356: .hasNext();) {
0357: Map.Entry element = (Map.Entry) iterator.next();
0358: List oldList = (List) element.getValue();
0359: List newList = (List) to.get(element.getKey());
0360: if (newList == null) {
0361: to.put(element.getKey(), new ArrayList(oldList));
0362: } else {
0363: addNonPrivateMethods(newList, oldList);
0364: }
0365: }
0366: }
0367:
0368: private void connectMultimethods(List super Classes) {
0369: super Classes = DefaultGroovyMethods.reverse(super Classes);
0370: Map last = null;
0371: for (Iterator iter = super Classes.iterator(); iter.hasNext();) {
0372: Class c = (Class) iter.next();
0373: Map methodIndex = (Map) classMethodIndex.get(c);
0374: if (methodIndex == last)
0375: continue;
0376: if (last != null)
0377: copyNonPrivateMethods(last, methodIndex);
0378: last = methodIndex;
0379: }
0380: }
0381:
0382: private void inheritMethods(Collection super Classes,
0383: Map classMethodIndex) {
0384: Map last = null;
0385: for (Iterator iter = super Classes.iterator(); iter.hasNext();) {
0386: Class c = (Class) iter.next();
0387: Map methodIndex = (Map) classMethodIndex.get(c);
0388: if (last != null) {
0389: if (methodIndex.size() == 0) {
0390: classMethodIndex.put(c, last);
0391: continue;
0392: }
0393: copyNonPrivateMethods(last, methodIndex);
0394: }
0395: last = methodIndex;
0396: }
0397: }
0398:
0399: private void addNonPrivateMethods(List newList, List oldList) {
0400: for (Iterator iter = oldList.iterator(); iter.hasNext();) {
0401: MetaMethod element = (MetaMethod) iter.next();
0402: if (element.isPrivate())
0403: continue;
0404: addMethodToList(newList, element);
0405: }
0406: }
0407:
0408: /**
0409: * @return all the normal instance methods avaiable on this class for the
0410: * given name
0411: */
0412: private List getMethods(Class sender, String name,
0413: boolean isCallToSuper) {
0414: Map methodIndex;
0415: if (isCallToSuper) {
0416: methodIndex = (Map) classMethodIndexForSuper.get(sender);
0417: } else {
0418: methodIndex = (Map) classMethodIndex.get(sender);
0419: }
0420: List answer;
0421: if (methodIndex != null) {
0422: answer = (List) methodIndex.get(name);
0423: if (answer == null)
0424: answer = Collections.EMPTY_LIST;
0425: } else {
0426: answer = Collections.EMPTY_LIST;
0427: }
0428:
0429: if (!isCallToSuper
0430: && GroovyCategorySupport.hasCategoryInAnyThread()) {
0431: List used = GroovyCategorySupport.getCategoryMethods(
0432: sender, name);
0433: if (used != null) {
0434: answer = new ArrayList(answer);
0435: for (Iterator iter = used.iterator(); iter.hasNext();) {
0436: MetaMethod element = (MetaMethod) iter.next();
0437: removeMatchingMethod(answer, element);
0438: }
0439: answer.addAll(used);
0440: }
0441: }
0442: return answer;
0443: }
0444:
0445: /**
0446: * @return all the normal static methods avaiable on this class for the
0447: * given name
0448: */
0449: private List getStaticMethods(Class sender, String name) {
0450: Map methodIndex = (Map) classStaticMethodIndex.get(sender);
0451: if (methodIndex == null)
0452: return Collections.EMPTY_LIST;
0453: List answer = (List) methodIndex.get(name);
0454: if (answer == null)
0455: return Collections.EMPTY_LIST;
0456: return answer;
0457: }
0458:
0459: public void addNewInstanceMethod(Method method) {
0460: NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(
0461: createMetaMethod(method));
0462: if (!newGroovyMethodsList.contains(newMethod)) {
0463: newGroovyMethodsList.add(newMethod);
0464: addMetaMethod(newMethod);
0465: }
0466: }
0467:
0468: public void addNewStaticMethod(Method method) {
0469: NewStaticMetaMethod newMethod = new NewStaticMetaMethod(
0470: createMetaMethod(method));
0471: if (!newGroovyMethodsList.contains(newMethod)) {
0472: newGroovyMethodsList.add(newMethod);
0473: addMetaMethod(newMethod);
0474: }
0475: }
0476:
0477: private void unwrap(Object[] arguments) {
0478: //
0479: // Temp code to ignore wrapped parameters
0480: // The New MOP will deal with these properly
0481: //
0482: for (int i = 0; i != arguments.length; i++) {
0483: if (arguments[i] instanceof Wrapper) {
0484: arguments[i] = ((Wrapper) arguments[i]).unwrap();
0485: }
0486: }
0487: }
0488:
0489: /**
0490: * Invokes the given method on the object.
0491: * @deprecated
0492: */
0493: public Object invokeMethod(Object object, String methodName,
0494: Object[] originalArguments) {
0495: return invokeMethod(theClass, object, methodName,
0496: originalArguments, false, false);
0497: }
0498:
0499: /**
0500: * Invokes the given method on the object.
0501: *
0502: */
0503: public Object invokeMethod(Class sender, Object object,
0504: String methodName, Object[] originalArguments,
0505: boolean isCallToSuper, boolean fromInsideClass) {
0506: checkInitalised();
0507: if (object == null) {
0508: throw new NullPointerException("Cannot invoke method: "
0509: + methodName + " on null object");
0510: }
0511: if (log.isLoggable(Level.FINER)) {
0512: MetaClassHelper.logMethodCall(object, methodName,
0513: originalArguments);
0514: }
0515: Object[] arguments = originalArguments;
0516: if (arguments == null)
0517: arguments = EMPTY_ARGUMENTS;
0518: Class[] argClasses = MetaClassHelper
0519: .convertToTypeArray(arguments);
0520: unwrap(arguments);
0521:
0522: MetaMethod method = getMethodWithCaching(sender, methodName,
0523: argClasses, isCallToSuper);
0524:
0525: if (method == null && arguments.length == 1
0526: && arguments[0] instanceof List) {
0527: Object[] newArguments = ((List) arguments[0]).toArray();
0528: Class[] newArgClasses = MetaClassHelper
0529: .convertToTypeArray(newArguments);
0530: method = getMethodWithCaching(sender, methodName,
0531: newArgClasses, isCallToSuper);
0532: if (method != null) {
0533: MethodKey methodKey = new DefaultMethodKey(sender,
0534: methodName, argClasses, isCallToSuper);
0535: method = new TransformMetaMethod(method) {
0536: public Object invoke(Object object,
0537: Object[] arguments) {
0538: Object firstArgument = arguments[0];
0539: List list = (List) firstArgument;
0540: arguments = list.toArray();
0541: return super .invoke(object, arguments);
0542: }
0543: };
0544: cacheInstanceMethod(methodKey, method);
0545: return invokeMethod(sender, object, methodName,
0546: originalArguments, isCallToSuper,
0547: fromInsideClass);
0548: }
0549: }
0550:
0551: boolean isClosure = object instanceof Closure;
0552: if (isClosure) {
0553: Closure closure = (Closure) object;
0554: Object delegate = closure.getDelegate();
0555: Object owner = closure.getOwner();
0556:
0557: if ("call".equals(methodName)
0558: || "doCall".equals(methodName)) {
0559: if (object.getClass() == MethodClosure.class) {
0560: MethodClosure mc = (MethodClosure) object;
0561: methodName = mc.getMethod();
0562: Class ownerClass = owner.getClass();
0563: if (owner instanceof Class)
0564: ownerClass = (Class) owner;
0565: MetaClass ownerMetaClass = registry
0566: .getMetaClass(ownerClass);
0567: return ownerMetaClass.invokeMethod(ownerClass,
0568: owner, methodName, arguments, false, false);
0569: } else if (object.getClass() == CurriedClosure.class) {
0570: CurriedClosure cc = (CurriedClosure) object;
0571: // change the arguments for an uncurried call
0572: arguments = cc.getUncurriedArguments(arguments);
0573: Class ownerClass = owner.getClass();
0574: if (owner instanceof Class)
0575: ownerClass = (Class) owner;
0576: MetaClass ownerMetaClass = registry
0577: .getMetaClass(ownerClass);
0578: return ownerMetaClass.invokeMethod(owner,
0579: methodName, arguments);
0580: }
0581: } else if ("curry".equals(methodName)) {
0582: return closure.curry(arguments);
0583: }
0584:
0585: if (method == null && owner != closure) {
0586: Class ownerClass = owner.getClass();
0587: if (owner instanceof Class)
0588: ownerClass = (Class) owner;
0589: MetaClass ownerMetaClass = registry
0590: .getMetaClass(ownerClass);
0591: method = ownerMetaClass.pickMethod(methodName,
0592: argClasses);
0593: if (method != null)
0594: return ownerMetaClass.invokeMethod(owner,
0595: methodName, originalArguments);
0596: }
0597: if (method == null && delegate != closure
0598: && delegate != null) {
0599: Class delegateClass = delegate.getClass();
0600: if (delegate instanceof Class)
0601: delegateClass = (Class) delegate;
0602: MetaClass delegateMetaClass = registry
0603: .getMetaClass(delegateClass);
0604: method = delegateMetaClass.pickMethod(methodName,
0605: argClasses);
0606: if (method != null)
0607: return delegateMetaClass.invokeMethod(delegate,
0608: methodName, originalArguments);
0609: }
0610: if (method == null) {
0611: // still no methods found, test if delegate or owner are GroovyObjects
0612: // and invoke the method on them if so.
0613: MissingMethodException last = null;
0614: if (owner != closure && (owner instanceof GroovyObject)) {
0615: try {
0616: GroovyObject go = (GroovyObject) owner;
0617: return go.invokeMethod(methodName,
0618: originalArguments);
0619: } catch (MissingMethodException mme) {
0620: if (last == null)
0621: last = mme;
0622: }
0623: }
0624: if (delegate != closure
0625: && (delegate instanceof GroovyObject)) {
0626: try {
0627: GroovyObject go = (GroovyObject) delegate;
0628: return go.invokeMethod(methodName,
0629: originalArguments);
0630: } catch (MissingMethodException mme) {
0631: last = mme;
0632: }
0633: }
0634: if (last != null)
0635: throw last;
0636: }
0637:
0638: }
0639:
0640: if (method != null) {
0641: return MetaClassHelper.doMethodInvoke(object, method,
0642: arguments);
0643: } else {
0644: // if no method was found, try to find a closure defined as a field of the class and run it
0645: try {
0646: Object value = this .getProperty(object, methodName);
0647: if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
0648: Closure closure = (Closure) value;
0649: MetaClass delegateMetaClass = closure
0650: .getMetaClass();
0651: return delegateMetaClass.invokeMethod(closure
0652: .getClass(), closure, "doCall",
0653: originalArguments, false, fromInsideClass);
0654: }
0655: } catch (MissingPropertyException mpe) {
0656: }
0657:
0658: throw new MissingMethodException(methodName, theClass,
0659: originalArguments, false);
0660: }
0661: }
0662:
0663: public MetaMethod getMethodWithCaching(Class sender,
0664: String methodName, Class[] arguments, boolean isCallToSuper) {
0665: // lets try use the cache to find the method
0666: if (GroovyCategorySupport.hasCategoryInAnyThread()
0667: && !isCallToSuper) {
0668: return getMethodWithoutCaching(sender, methodName,
0669: arguments, isCallToSuper);
0670: } else {
0671: MethodKey methodKey = new DefaultMethodKey(sender,
0672: methodName, arguments, isCallToSuper);
0673: MetaMethod method = (MetaMethod) methodCache.get(methodKey);
0674: if (method == null) {
0675: method = getMethodWithoutCaching(sender, methodName,
0676: arguments, isCallToSuper);
0677: cacheInstanceMethod(methodKey, method);
0678: }
0679: return method;
0680: }
0681: }
0682:
0683: protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
0684: if (method != null && method.isCacheable()) {
0685: methodCache.put(key, method);
0686: }
0687: }
0688:
0689: protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
0690: if (method != null && method.isCacheable()) {
0691: staticMethodCache.put(key, method);
0692: }
0693: }
0694:
0695: public Constructor retrieveConstructor(Class[] arguments) {
0696: Constructor constructor = (Constructor) chooseMethod("<init>",
0697: constructors, arguments, false);
0698: if (constructor != null) {
0699: return constructor;
0700: }
0701: constructor = (Constructor) chooseMethod("<init>",
0702: constructors, arguments, true);
0703: if (constructor != null) {
0704: return constructor;
0705: }
0706: return null;
0707: }
0708:
0709: public MetaMethod retrieveStaticMethod(String methodName,
0710: Class[] arguments) {
0711: MethodKey methodKey = new DefaultMethodKey(theClass,
0712: methodName, arguments, false);
0713: MetaMethod method = (MetaMethod) staticMethodCache
0714: .get(methodKey);
0715: if (method == null) {
0716: method = pickStaticMethod(theClass, methodName, arguments);
0717: cacheStaticMethod(methodKey, method);
0718: }
0719: return method;
0720: }
0721:
0722: public MetaMethod getMethodWithoutCaching(Class sender,
0723: String methodName, Class[] arguments, boolean isCallToSuper) {
0724: MetaMethod method = null;
0725: List methods = getMethods(sender, methodName, isCallToSuper);
0726: if (methods != null && !methods.isEmpty()) {
0727: method = (MetaMethod) chooseMethod(methodName, methods,
0728: arguments, false);
0729: }
0730: return method;
0731: }
0732:
0733: public Object invokeStaticMethod(Object object, String methodName,
0734: Object[] arguments) {
0735: checkInitalised();
0736: if (log.isLoggable(Level.FINER)) {
0737: MetaClassHelper
0738: .logMethodCall(object, methodName, arguments);
0739: }
0740:
0741: Class sender = object.getClass();
0742: if (object instanceof Class)
0743: sender = (Class) object;
0744: if (sender != theClass) {
0745: MetaClass mc = registry.getMetaClass(sender);
0746: return mc.invokeStaticMethod(sender, methodName, arguments);
0747: }
0748: if (sender == Class.class) {
0749: return invokeMethod(object, methodName, arguments);
0750: }
0751:
0752: if (arguments == null)
0753: arguments = EMPTY_ARGUMENTS;
0754: Class[] argClasses = MetaClassHelper
0755: .convertToTypeArray(arguments);
0756: unwrap(arguments);
0757:
0758: // lets try use the cache to find the method
0759: MethodKey methodKey = new DefaultMethodKey(sender, methodName,
0760: argClasses, false);
0761: MetaMethod method = (MetaMethod) staticMethodCache
0762: .get(methodKey);
0763: if (method == null) {
0764: method = pickStaticMethod(sender, methodName, argClasses);
0765: cacheStaticMethod(methodKey.createCopy(), method);
0766: }
0767:
0768: if (method != null) {
0769: return MetaClassHelper.doMethodInvoke(object, method,
0770: arguments);
0771: }
0772:
0773: throw new MissingMethodException(methodName, sender, arguments,
0774: true);
0775: }
0776:
0777: private MetaMethod pickStaticMethod(Class sender,
0778: String methodName, Class[] arguments) {
0779: MetaMethod method = null;
0780: List methods = getStaticMethods(sender, methodName);
0781:
0782: if (!methods.isEmpty()) {
0783: method = (MetaMethod) chooseMethod(methodName, methods,
0784: arguments, false);
0785: }
0786: if (method == null && theClass != Class.class) {
0787: MetaClass classMetaClass = registry
0788: .getMetaClass(Class.class);
0789: method = classMetaClass.pickMethod(methodName, arguments);
0790: }
0791: if (method == null) {
0792: method = (MetaMethod) chooseMethod(methodName, methods,
0793: MetaClassHelper.convertToTypeArray(arguments), true);
0794: }
0795: return method;
0796: }
0797:
0798: public Object invokeConstructor(Object[] arguments) {
0799: return invokeConstructor(theClass, arguments, false);
0800: }
0801:
0802: public int selectConstructorAndTransformArguments(
0803: int numberOfCosntructors, Object[] arguments) {
0804: //TODO: that is just a quick prototype, not the real thing!
0805: if (numberOfCosntructors != constructors.size()) {
0806: throw new IncompatibleClassChangeError(
0807: "the number of constructors during runtime and compile time for "
0808: + this .theClass.getName()
0809: + " do not match. Expected "
0810: + numberOfCosntructors + " but got "
0811: + constructors.size());
0812: }
0813:
0814: if (arguments == null)
0815: arguments = EMPTY_ARGUMENTS;
0816: Class[] argClasses = MetaClassHelper
0817: .convertToTypeArray(arguments);
0818: unwrap(arguments);
0819: Constructor constructor = (Constructor) chooseMethod("<init>",
0820: constructors, argClasses, false);
0821: if (constructor == null) {
0822: constructor = (Constructor) chooseMethod("<init>",
0823: constructors, argClasses, true);
0824: }
0825: if (constructor == null) {
0826: throw new GroovyRuntimeException(
0827: "Could not find matching constructor for: "
0828: + theClass.getName() + "("
0829: + InvokerHelper.toTypeString(arguments)
0830: + ")");
0831: }
0832: List l = new ArrayList(constructors);
0833: Comparator comp = new Comparator() {
0834: public int compare(Object arg0, Object arg1) {
0835: Constructor c0 = (Constructor) arg0;
0836: Constructor c1 = (Constructor) arg1;
0837: String descriptor0 = BytecodeHelper
0838: .getMethodDescriptor(Void.TYPE, c0
0839: .getParameterTypes());
0840: String descriptor1 = BytecodeHelper
0841: .getMethodDescriptor(Void.TYPE, c1
0842: .getParameterTypes());
0843: return descriptor0.compareTo(descriptor1);
0844: }
0845: };
0846: Collections.sort(l, comp);
0847: int found = -1;
0848: for (int i = 0; i < l.size(); i++) {
0849: if (l.get(i) != constructor)
0850: continue;
0851: found = i;
0852: break;
0853: }
0854: // NOTE: must be changed to "1 |" if constructor was vargs
0855: int ret = 0 | (found << 8);
0856: return ret;
0857: }
0858:
0859: /**
0860: * checks if the initialisation of the class id complete.
0861: * This method should be called as a form of assert, it is no
0862: * way to test if there is still initialisation work to be done.
0863: * Such logic must be implemented in a different way.
0864: * @throws IllegalStateException if the initialisation is incomplete yet
0865: */
0866: protected void checkInitalised() {
0867: if (!isInitialized())
0868: throw new IllegalStateException(
0869: "initialize must be called for meta "
0870: + "class of "
0871: + theClass
0872: + "("
0873: + this .getClass()
0874: + ") "
0875: + "to complete initialisation process "
0876: + "before any invocation or field/property "
0877: + "access can be done");
0878: }
0879:
0880: private Object invokeConstructor(Class at, Object[] arguments,
0881: boolean setAccessible) {
0882: checkInitalised();
0883: if (arguments == null)
0884: arguments = EMPTY_ARGUMENTS;
0885: Class[] argClasses = MetaClassHelper
0886: .convertToTypeArray(arguments);
0887: unwrap(arguments);
0888: Constructor constructor = (Constructor) chooseMethod("<init>",
0889: constructors, argClasses, false);
0890: if (constructor != null) {
0891: return doConstructorInvoke(at, constructor, arguments, true);
0892: }
0893: constructor = (Constructor) chooseMethod("<init>",
0894: constructors, argClasses, true);
0895: if (constructor != null) {
0896: return doConstructorInvoke(at, constructor, arguments, true);
0897: }
0898:
0899: if (arguments.length == 1) {
0900: Object firstArgument = arguments[0];
0901: if (firstArgument instanceof Map) {
0902: constructor = (Constructor) chooseMethod("<init>",
0903: constructors, MetaClassHelper.EMPTY_TYPE_ARRAY,
0904: false);
0905: if (constructor != null) {
0906: Object bean = doConstructorInvoke(at, constructor,
0907: MetaClassHelper.EMPTY_ARRAY, true);
0908: setProperties(bean, ((Map) firstArgument));
0909: return bean;
0910: }
0911: }
0912: }
0913: throw new GroovyRuntimeException(
0914: "Could not find matching constructor for: "
0915: + theClass.getName() + "("
0916: + InvokerHelper.toTypeString(arguments) + ")");
0917: }
0918:
0919: /**
0920: * Sets a number of bean properties from the given Map where the keys are
0921: * the String names of properties and the values are the values of the
0922: * properties to set
0923: */
0924: public void setProperties(Object bean, Map map) {
0925: checkInitalised();
0926: for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
0927: Map.Entry entry = (Map.Entry) iter.next();
0928: String key = entry.getKey().toString();
0929:
0930: Object value = entry.getValue();
0931: setProperty(bean, key, value);
0932: }
0933: }
0934:
0935: /**
0936: * @return the given property's value on the object
0937: */
0938: public Object getProperty(Class sender, Object object, String name,
0939: boolean useSuper, boolean fromInsideClass) {
0940: checkInitalised();
0941:
0942: //----------------------------------------------------------------------
0943: // handling of static
0944: //----------------------------------------------------------------------
0945: boolean isStatic = theClass != Class.class
0946: && object instanceof Class;
0947: if (isStatic && object != theClass) {
0948: MetaClass mc = registry.getMetaClass((Class) object);
0949: return mc
0950: .getProperty(sender, object, name, useSuper, false);
0951: }
0952:
0953: MetaMethod method = null;
0954: Object[] arguments = EMPTY_ARGUMENTS;
0955:
0956: //----------------------------------------------------------------------
0957: // getter
0958: //----------------------------------------------------------------------
0959: MetaProperty mp = getMetaProperty(sender, name, useSuper,
0960: isStatic);
0961: if (mp != null) {
0962: if (mp instanceof MetaBeanProperty) {
0963: MetaBeanProperty mbp = (MetaBeanProperty) mp;
0964: method = mbp.getGetter();
0965: mp = mbp.getField();
0966: }
0967: }
0968:
0969: // check for a category method named like a getter
0970: if (method == null && !useSuper && !isStatic
0971: && GroovyCategorySupport.hasCategoryInAnyThread()) {
0972: String getterName = "get"
0973: + MetaClassHelper.capitalize(name);
0974: method = getCategoryMethodGetter(sender, getterName, false);
0975: }
0976:
0977: //----------------------------------------------------------------------
0978: // field
0979: //----------------------------------------------------------------------
0980: if (method == null && mp != null) {
0981: return mp.getProperty(object);
0982: }
0983:
0984: //----------------------------------------------------------------------
0985: // generic get method
0986: //----------------------------------------------------------------------
0987: // check for a generic get method provided through a category
0988: if (method == null && !useSuper && !isStatic
0989: && GroovyCategorySupport.hasCategoryInAnyThread()) {
0990: method = getCategoryMethodGetter(sender, "get", true);
0991: if (method != null)
0992: arguments = new Object[] { name };
0993: }
0994:
0995: // the generic method is valid, if available (!=null), if static or
0996: // if it is not static and we do no static access
0997: if (method == null && genericGetMethod != null
0998: && !(!genericGetMethod.isStatic() && isStatic)) {
0999: arguments = new Object[] { name };
1000: method = genericGetMethod;
1001: }
1002:
1003: //----------------------------------------------------------------------
1004: // special cases
1005: //----------------------------------------------------------------------
1006: if (method == null) {
1007: /** todo these special cases should be special MetaClasses maybe */
1008: if (theClass != Class.class && object instanceof Class) {
1009: MetaClass mc = registry.getMetaClass(Class.class);
1010: return mc.getProperty(Class.class, object, name,
1011: useSuper, false);
1012: } else if (object instanceof Collection) {
1013: return DefaultGroovyMethods.getAt((Collection) object,
1014: name);
1015: } else if (object instanceof Object[]) {
1016: return DefaultGroovyMethods.getAt(Arrays
1017: .asList((Object[]) object), name);
1018: } else {
1019: MetaMethod addListenerMethod = (MetaMethod) listeners
1020: .get(name);
1021: if (addListenerMethod != null) {
1022: //TODO: one day we could try return the previously registered Closure listener for easy removal
1023: return null;
1024: }
1025: }
1026: } else {
1027:
1028: //----------------------------------------------------------------------
1029: // executing the getter method
1030: //----------------------------------------------------------------------
1031: return MetaClassHelper.doMethodInvoke(object, method,
1032: arguments);
1033: }
1034:
1035: //----------------------------------------------------------------------
1036: // error due to missing method/field
1037: //----------------------------------------------------------------------
1038: throw new MissingPropertyException(name, theClass);
1039: }
1040:
1041: private MetaMethod getCategoryMethodGetter(Class sender,
1042: String name, boolean useLongVersion) {
1043: List possibleGenericMethods = GroovyCategorySupport
1044: .getCategoryMethods(sender, name);
1045: if (possibleGenericMethods != null) {
1046: for (Iterator iter = possibleGenericMethods.iterator(); iter
1047: .hasNext();) {
1048: MetaMethod mmethod = (MetaMethod) iter.next();
1049: Class[] paramTypes = mmethod.getParameterTypes();
1050: if (useLongVersion) {
1051: if (paramTypes.length == 1
1052: && paramTypes[0] == String.class) {
1053: return mmethod;
1054: }
1055: } else {
1056: if (paramTypes.length == 0)
1057: return mmethod;
1058: }
1059: }
1060: }
1061: return null;
1062: }
1063:
1064: private MetaMethod getCategoryMethodSetter(Class sender,
1065: String name, boolean useLongVersion) {
1066: List possibleGenericMethods = GroovyCategorySupport
1067: .getCategoryMethods(sender, name);
1068: if (possibleGenericMethods != null) {
1069: for (Iterator iter = possibleGenericMethods.iterator(); iter
1070: .hasNext();) {
1071: MetaMethod mmethod = (MetaMethod) iter.next();
1072: Class[] paramTypes = mmethod.getParameterTypes();
1073: if (useLongVersion) {
1074: if (paramTypes.length == 2
1075: && paramTypes[0] == String.class) {
1076: return mmethod;
1077: }
1078: } else {
1079: if (paramTypes.length == 1)
1080: return mmethod;
1081: }
1082: }
1083: }
1084: return null;
1085: }
1086:
1087: /**
1088: * Get all the properties defined for this type
1089: * @return a list of MetaProperty objects
1090: */
1091: public List getProperties() {
1092: checkInitalised();
1093: Map propertyMap = (Map) classPropertyIndex.get(theClass);
1094: // simply return the values of the metaproperty map as a List
1095: List ret = new ArrayList(propertyMap.size());
1096: for (Iterator iter = propertyMap.values().iterator(); iter
1097: .hasNext();) {
1098: MetaProperty element = (MetaProperty) iter.next();
1099: if (element instanceof MetaFieldProperty)
1100: continue;
1101: // filter out DGM beans
1102: if (element instanceof MetaBeanProperty) {
1103: MetaBeanProperty mp = (MetaBeanProperty) element;
1104: boolean setter = true;
1105: boolean getter = true;
1106: if (mp.getGetter() == null
1107: || mp.getGetter() instanceof NewInstanceMetaMethod) {
1108: getter = false;
1109: }
1110: if (mp.getSetter() == null
1111: || mp.getSetter() instanceof NewInstanceMetaMethod) {
1112: setter = false;
1113: }
1114: if (!setter && !getter)
1115: continue;
1116: if (!setter && mp.getSetter() != null) {
1117: element = new MetaBeanProperty(mp.getName(), mp
1118: .getType(), mp.getGetter(), null);
1119: }
1120: if (!getter && mp.getGetter() != null) {
1121: element = new MetaBeanProperty(mp.getName(), mp
1122: .getType(), null, mp.getSetter());
1123: }
1124: }
1125: ret.add(element);
1126: }
1127: return ret;
1128: }
1129:
1130: private MetaMethod findPropertyMethod(List methods, boolean isGetter) {
1131: LinkedList ret = new LinkedList();
1132: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1133: MetaMethod element = (MetaMethod) iter.next();
1134: if (!isGetter &&
1135: //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
1136: element.getParameterTypes().length == 1) {
1137: ret.add(element);
1138: }
1139: if (isGetter
1140: && !(element.getReturnType() == Void.class || element
1141: .getReturnType() == Void.TYPE)
1142: && element.getParameterTypes().length == 0) {
1143: ret.add(element);
1144: }
1145: }
1146: if (ret.size() == 0)
1147: return null;
1148: if (ret.size() == 1)
1149: return (MetaMethod) ret.getFirst();
1150:
1151: // we found multiple matching methods
1152: // this is a problem, because we can use only one
1153: // if it is a getter, then use the most general return
1154: // type to decide which method to use. If it is a setter
1155: // we use the type of the first parameter
1156: MetaMethod method = null;
1157: int distance = -1;
1158: for (Iterator iter = ret.iterator(); iter.hasNext();) {
1159: MetaMethod element = (MetaMethod) iter.next();
1160: Class c;
1161: if (isGetter) {
1162: c = element.getReturnType();
1163: } else {
1164: c = element.getParameterTypes()[0];
1165: }
1166: int localDistance = distanceToObject(c);
1167: //TODO: maybe implement the case localDistance==distance
1168: if (distance == -1 || distance > localDistance) {
1169: distance = localDistance;
1170: method = element;
1171: }
1172: }
1173: return method;
1174: }
1175:
1176: private static int distanceToObject(Class c) {
1177: int count;
1178: for (count = 0; c != null; count++) {
1179: c = c.getSuperclass();
1180: }
1181: return count;
1182: }
1183:
1184: /**
1185: * This will build up the property map (Map of MetaProperty objects, keyed on
1186: * property name).
1187: */
1188: private void setupProperties(
1189: PropertyDescriptor[] propertyDescriptors) {
1190: LinkedList super Classes = getSuperClasses();
1191: Set interfaces = new HashSet();
1192: makeInterfaceSet(theClass, interfaces);
1193:
1194: // if this an Array, then add the special read-only "length" property
1195: if (theClass.isArray()) {
1196: Map map = new HashMap();
1197: map.put("length", arrayLengthProperty);
1198: classPropertyIndex.put(theClass, map);
1199: }
1200:
1201: inheritStaticInterfaceFields(super Classes, interfaces);
1202: inheritFields(super Classes);
1203: applyPropertyDescriptors(propertyDescriptors);
1204:
1205: applyStrayPropertyMethods(super Classes, classMethodIndex,
1206: classPropertyIndex);
1207: applyStrayPropertyMethods(super Classes,
1208: classMethodIndexForSuper, classPropertyIndexForSuper);
1209:
1210: copyClassPropertyIndexForSuper();
1211: makeStaticPropertyIndex();
1212: }
1213:
1214: private void makeStaticPropertyIndex() {
1215: Map propertyMap = (Map) classPropertyIndex.get(theClass);
1216: for (Iterator iter = propertyMap.entrySet().iterator(); iter
1217: .hasNext();) {
1218: Map.Entry entry = (Map.Entry) iter.next();
1219: MetaProperty mp = (MetaProperty) entry.getValue();
1220: if (mp instanceof MetaFieldProperty) {
1221: MetaFieldProperty mfp = (MetaFieldProperty) mp;
1222: if (!mfp.isStatic())
1223: continue;
1224: } else if (mp instanceof MetaBeanProperty) {
1225: MetaBeanProperty mbp = (MetaBeanProperty) mp;
1226: boolean getter = mbp.getGetter() == null
1227: || mbp.getGetter().isStatic();
1228: boolean setter = mbp.getSetter() == null
1229: || mbp.getSetter().isStatic();
1230: boolean field = mbp.getField() == null
1231: || mbp.getField().isStatic();
1232:
1233: if (!getter && !setter && !field) {
1234: continue;
1235: } else if (setter && getter) {
1236: if (field) {
1237: mp = mbp; // nothing to do
1238: } else {
1239: mp = new MetaBeanProperty(mbp.getName(), mbp
1240: .getType(), mbp.getGetter(), mbp
1241: .getSetter());
1242: }
1243: } else if (getter && !setter) {
1244: if (mbp.getGetter() == null) {
1245: mp = mbp.getField();
1246: } else {
1247: MetaBeanProperty newmp = new MetaBeanProperty(
1248: mbp.getName(), mbp.getType(), mbp
1249: .getGetter(), null);
1250: if (field)
1251: newmp.setField(mbp.getField());
1252: mp = newmp;
1253: }
1254: } else if (setter && !getter) {
1255: if (mbp.getSetter() == null) {
1256: mp = mbp.getField();
1257: } else {
1258: MetaBeanProperty newmp = new MetaBeanProperty(
1259: mbp.getName(), mbp.getType(), null, mbp
1260: .getSetter());
1261: if (field)
1262: newmp.setField(mbp.getField());
1263: mp = newmp;
1264: }
1265: } else if (field) {
1266: mp = mbp.getField();
1267: }
1268: } else {
1269: continue; // ignore all other types
1270: }
1271: if (mp == null)
1272: continue;
1273: staticPropertyIndex.put(entry.getKey(), mp);
1274: }
1275:
1276: }
1277:
1278: private void copyClassPropertyIndexForSuper() {
1279: for (Iterator iter = classPropertyIndex.entrySet().iterator(); iter
1280: .hasNext();) {
1281: Map.Entry entry = (Map.Entry) iter.next();
1282: HashMap newVal = new HashMap((Map) entry.getValue());
1283: classPropertyIndexForSuper.put(entry.getKey(), newVal);
1284: }
1285: }
1286:
1287: private Map getMap2MapNotNull(Map m, Object key) {
1288: Map ret = (Map) m.get(key);
1289: if (ret == null) {
1290: ret = new HashMap();
1291: m.put(key, ret);
1292: }
1293: return ret;
1294: }
1295:
1296: private void inheritStaticInterfaceFields(LinkedList super Classes,
1297: Set interfaces) {
1298: for (Iterator interfaceIter = interfaces.iterator(); interfaceIter
1299: .hasNext();) {
1300: Class iclass = (Class) interfaceIter.next();
1301: Map iPropertyIndex = getMap2MapNotNull(classPropertyIndex,
1302: iclass);
1303: addFields(iclass, iPropertyIndex);
1304: for (Iterator classIter = super Classes.iterator(); classIter
1305: .hasNext();) {
1306: Class sclass = (Class) classIter.next();
1307: if (!iclass.isAssignableFrom(sclass))
1308: continue;
1309: Map sPropertyIndex = getMap2MapNotNull(
1310: classPropertyIndex, sclass);
1311: copyNonPrivateFields(iPropertyIndex, sPropertyIndex);
1312: }
1313: }
1314: }
1315:
1316: private void inheritFields(LinkedList super Classes) {
1317: Map last = null;
1318: for (Iterator iter = super Classes.iterator(); iter.hasNext();) {
1319: Class klass = (Class) iter.next();
1320: Map propertyIndex = getMap2MapNotNull(classPropertyIndex,
1321: klass);
1322: if (last != null) {
1323: copyNonPrivateFields(last, propertyIndex);
1324: }
1325: last = propertyIndex;
1326: addFields(klass, propertyIndex);
1327: }
1328: }
1329:
1330: private void addFields(final Class klass, Map propertyIndex) {
1331: Field[] fields = (Field[]) AccessController
1332: .doPrivileged(new PrivilegedAction() {
1333: public Object run() {
1334: return klass.getDeclaredFields();
1335: }
1336: });
1337: for (int i = 0; i < fields.length; i++) {
1338: MetaFieldProperty mfp = new MetaFieldProperty(fields[i]);
1339: propertyIndex.put(fields[i].getName(), mfp);
1340: }
1341: }
1342:
1343: private void copyNonPrivateFields(Map from, Map to) {
1344: for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) {
1345: Map.Entry entry = (Map.Entry) iter.next();
1346: MetaFieldProperty mfp = (MetaFieldProperty) entry
1347: .getValue();
1348: if (!Modifier.isPublic(mfp.getModifiers())
1349: && !Modifier.isProtected(mfp.getModifiers()))
1350: continue;
1351: to.put(entry.getKey(), mfp);
1352: }
1353: }
1354:
1355: private void applyStrayPropertyMethods(LinkedList super Classes,
1356: Map classMethodIndex, Map classPropertyIndex) {
1357: // now look for any stray getters that may be used to define a property
1358: for (Iterator iter = super Classes.iterator(); iter.hasNext();) {
1359: Class klass = (Class) iter.next();
1360: Map methodIndex = (Map) classMethodIndex.get(klass);
1361: Map propertyIndex = getMap2MapNotNull(classPropertyIndex,
1362: klass);
1363: for (Iterator nameMethodIterator = methodIndex.entrySet()
1364: .iterator(); nameMethodIterator.hasNext();) {
1365: Map.Entry entry = (Map.Entry) nameMethodIterator.next();
1366: String methodName = (String) entry.getKey();
1367: // name too sort?
1368: if (methodName.length() < 4)
1369: continue;
1370: //possible getter/setter
1371: boolean isGetter = methodName.startsWith("get");
1372: boolean isSetter = methodName.startsWith("set");
1373: if (!isGetter && !isSetter)
1374: continue;
1375:
1376: // get the name of the property
1377: String propName = methodName.substring(3, 4)
1378: .toLowerCase()
1379: + methodName.substring(4);
1380: MetaMethod propertyMethod = findPropertyMethod(
1381: (List) entry.getValue(), isGetter);
1382: if (propertyMethod == null)
1383: continue;
1384:
1385: createMetaBeanProperty(propertyIndex, propName,
1386: isGetter, propertyMethod);
1387: }
1388: }
1389: }
1390:
1391: private void createMetaBeanProperty(Map propertyIndex,
1392: String propName, boolean isGetter, MetaMethod propertyMethod) {
1393: // is this property already accounted for?
1394: MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
1395: if (mp == null) {
1396: if (isGetter) {
1397: mp = new MetaBeanProperty(propName, propertyMethod
1398: .getReturnType(), propertyMethod, null);
1399: } else {
1400: //isSetter
1401: mp = new MetaBeanProperty(propName, propertyMethod
1402: .getParameterTypes()[0], null, propertyMethod);
1403: }
1404: } else {
1405: MetaBeanProperty mbp;
1406: MetaFieldProperty mfp;
1407: if (mp instanceof MetaBeanProperty) {
1408: mbp = (MetaBeanProperty) mp;
1409: mfp = mbp.getField();
1410: } else if (mp instanceof MetaFieldProperty) {
1411: mfp = (MetaFieldProperty) mp;
1412: mbp = new MetaBeanProperty(propName, mfp.getType(),
1413: null, null);
1414: } else {
1415: throw new GroovyBugError(
1416: "unknown MetaProperty class used. Class is "
1417: + mp.getClass());
1418: }
1419: // we may have already found one for this name
1420: if (isGetter && mbp.getGetter() == null) {
1421: mbp.setGetter(propertyMethod);
1422: } else if (!isGetter && mbp.getSetter() == null) {
1423: mbp.setSetter(propertyMethod);
1424: }
1425: mbp.setField(mfp);
1426: mp = mbp;
1427: }
1428: propertyIndex.put(propName, mp);
1429: }
1430:
1431: private void applyPropertyDescriptors(
1432: PropertyDescriptor[] propertyDescriptors) {
1433: Map propertyMap = (Map) classPropertyIndex.get(theClass);
1434: // now iterate over the map of property descriptors and generate
1435: // MetaBeanProperty objects
1436: for (int i = 0; i < propertyDescriptors.length; i++) {
1437: PropertyDescriptor pd = propertyDescriptors[i];
1438:
1439: // skip if the property type is unknown (this seems to be the case if the
1440: // property descriptor is based on a setX() method that has two parameters,
1441: // which is not a valid property)
1442: if (pd.getPropertyType() == null)
1443: continue;
1444:
1445: // get the getter method
1446: Method method = pd.getReadMethod();
1447: MetaMethod getter;
1448: if (method != null)
1449: getter = findMethod(method);
1450: else
1451: getter = null;
1452:
1453: // get the setter method
1454: MetaMethod setter;
1455: method = pd.getWriteMethod();
1456: if (method != null)
1457: setter = findMethod(method);
1458: else
1459: setter = null;
1460:
1461: // now create the MetaProperty object
1462: MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd
1463: .getPropertyType(), getter, setter);
1464:
1465: //keep field
1466: MetaFieldProperty field = null;
1467: MetaProperty old = (MetaProperty) propertyMap.get(pd
1468: .getName());
1469: if (old != null) {
1470: if (old instanceof MetaBeanProperty) {
1471: field = ((MetaBeanProperty) old).getField();
1472: } else {
1473: field = (MetaFieldProperty) old;
1474: }
1475: mp.setField(field);
1476: }
1477:
1478: // put it in the list
1479: // this will overwrite a possible field property
1480: propertyMap.put(pd.getName(), mp);
1481: }
1482: }
1483:
1484: /**
1485: * Sets the property value on an object
1486: */
1487: public void setProperty(Class sender, Object object, String name,
1488: Object newValue, boolean useSuper, boolean fromInsideClass) {
1489: checkInitalised();
1490:
1491: //----------------------------------------------------------------------
1492: // handling of static
1493: //----------------------------------------------------------------------
1494: boolean isStatic = theClass != Class.class
1495: && object instanceof Class;
1496: if (isStatic && object != theClass) {
1497: MetaClass mc = registry.getMetaClass((Class) object);
1498: mc.getProperty(sender, object, name, useSuper,
1499: fromInsideClass);
1500: return;
1501: }
1502:
1503: //----------------------------------------------------------------------
1504: // Unwrap wrapped values fo now - the new MOP will handle them properly
1505: //----------------------------------------------------------------------
1506: if (newValue instanceof Wrapper)
1507: newValue = ((Wrapper) newValue).unwrap();
1508:
1509: MetaMethod method = null;
1510: Object[] arguments = null;
1511:
1512: //----------------------------------------------------------------------
1513: // setter
1514: //----------------------------------------------------------------------
1515: MetaProperty mp = getMetaProperty(sender, name, useSuper,
1516: isStatic);
1517: MetaProperty field = null;
1518: if (mp != null) {
1519: if (mp instanceof MetaBeanProperty) {
1520: MetaBeanProperty mbp = (MetaBeanProperty) mp;
1521: method = mbp.getSetter();
1522: if (method != null)
1523: arguments = new Object[] { newValue };
1524: field = mbp.getField();
1525: } else {
1526: field = mp;
1527: }
1528: }
1529:
1530: // check for a category method named like a setter
1531: if (!useSuper && !isStatic
1532: && GroovyCategorySupport.hasCategoryInAnyThread()) {
1533: String getterName = "set"
1534: + MetaClassHelper.capitalize(name);
1535: method = getCategoryMethodSetter(sender, getterName, false);
1536: if (method != null)
1537: arguments = new Object[] { newValue };
1538: }
1539:
1540: //----------------------------------------------------------------------
1541: // listener method
1542: //----------------------------------------------------------------------
1543: boolean ambigousListener = false;
1544: boolean usesProxy = false;
1545: if (method == null) {
1546: method = (MetaMethod) listeners.get(name);
1547: ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
1548: if (method != null && !ambigousListener
1549: && newValue instanceof Closure) {
1550: // lets create a dynamic proxy
1551: Object proxy = MetaClassHelper.createListenerProxy(
1552: method.getParameterTypes()[0], name,
1553: (Closure) newValue);
1554: arguments = new Object[] { proxy };
1555: newValue = proxy;
1556: usesProxy = true;
1557: } else {
1558: method = null;
1559: }
1560: }
1561:
1562: //----------------------------------------------------------------------
1563: // field
1564: //----------------------------------------------------------------------
1565: if (method == null && field != null) {
1566: field.setProperty(object, newValue);
1567: return;
1568: }
1569:
1570: //----------------------------------------------------------------------
1571: // generic set method
1572: //----------------------------------------------------------------------
1573: // check for a generic get method provided through a category
1574: if (method == null && !useSuper && !isStatic
1575: && GroovyCategorySupport.hasCategoryInAnyThread()) {
1576: method = getCategoryMethodSetter(sender, "set", true);
1577: if (method != null)
1578: arguments = new Object[] { name, newValue };
1579: }
1580:
1581: // the generic method is valid, if available (!=null), if static or
1582: // if it is not static and we do no static access
1583: if (method == null && genericSetMethod != null
1584: && !(!genericSetMethod.isStatic() && isStatic)) {
1585: arguments = new Object[] { name, newValue };
1586: method = genericSetMethod;
1587: }
1588:
1589: //----------------------------------------------------------------------
1590: // executing the getter method
1591: //----------------------------------------------------------------------
1592: if (method != null) {
1593: if (arguments.length == 1) {
1594: newValue = DefaultTypeTransformation.castToType(
1595: newValue, method.getParameterTypes()[0]);
1596: arguments[0] = newValue;
1597: } else {
1598: newValue = DefaultTypeTransformation.castToType(
1599: newValue, method.getParameterTypes()[1]);
1600: arguments[1] = newValue;
1601: }
1602: MetaClassHelper.doMethodInvoke(object, method, arguments);
1603: return;
1604: }
1605:
1606: //----------------------------------------------------------------------
1607: // error due to missing method/field
1608: //----------------------------------------------------------------------
1609: if (ambigousListener) {
1610: throw new GroovyRuntimeException(
1611: "There are multiple listeners for the property "
1612: + name
1613: + ". Please do not use the bean short form to access this listener.");
1614: }
1615: throw new MissingPropertyException(name, theClass);
1616: }
1617:
1618: private MetaProperty getMetaProperty(Class clazz, String name,
1619: boolean useSuper, boolean useStatic) {
1620: Map propertyMap;
1621: if (useStatic) {
1622: propertyMap = staticPropertyIndex;
1623: } else if (useSuper) {
1624: propertyMap = (Map) classPropertyIndexForSuper.get(clazz);
1625: } else {
1626: propertyMap = (Map) classPropertyIndex.get(clazz);
1627: }
1628: if (propertyMap == null) {
1629: if (clazz != theClass) {
1630: return getMetaProperty(theClass, name, useSuper,
1631: useStatic);
1632: } else {
1633: return null;
1634: }
1635: }
1636: return (MetaProperty) propertyMap.get(name);
1637: }
1638:
1639: /**
1640: * Looks up the given attribute (field) on the given object
1641: */
1642: public Object getAttribute(Class sender, Object object,
1643: String attribute, boolean useSuper, boolean fromInsideClass) {
1644: checkInitalised();
1645:
1646: boolean isStatic = theClass != Class.class
1647: && object instanceof Class;
1648: if (isStatic && object != theClass) {
1649: MetaClass mc = registry.getMetaClass((Class) object);
1650: return mc.getAttribute(sender, object, attribute, useSuper);
1651: }
1652:
1653: MetaProperty mp = getMetaProperty(sender, attribute, useSuper,
1654: isStatic);
1655:
1656: if (mp != null) {
1657: if (mp instanceof MetaBeanProperty) {
1658: MetaBeanProperty mbp = (MetaBeanProperty) mp;
1659: mp = mbp.getField();
1660: }
1661: try {
1662: // delegate the get operation to the metaproperty
1663: if (mp != null)
1664: return mp.getProperty(object);
1665: } catch (Exception e) {
1666: throw new GroovyRuntimeException("Cannot read field: "
1667: + attribute, e);
1668: }
1669: }
1670:
1671: throw new MissingFieldException(attribute, theClass);
1672: }
1673:
1674: /**
1675: * Sets the given attribute (field) on the given object
1676: */
1677: public void setAttribute(Class sender, Object object,
1678: String attribute, Object newValue, boolean useSuper,
1679: boolean fromInsideClass) {
1680: checkInitalised();
1681:
1682: boolean isStatic = theClass != Class.class
1683: && object instanceof Class;
1684: if (isStatic && object != theClass) {
1685: MetaClass mc = registry.getMetaClass((Class) object);
1686: mc.setAttribute(sender, object, attribute, newValue,
1687: useSuper, fromInsideClass);
1688: return;
1689: }
1690:
1691: MetaProperty mp = getMetaProperty(sender, attribute, useSuper,
1692: isStatic);
1693:
1694: if (mp != null) {
1695: if (mp instanceof MetaBeanProperty) {
1696: MetaBeanProperty mbp = (MetaBeanProperty) mp;
1697: mp = mbp.getField();
1698: }
1699: if (mp != null) {
1700: mp.setProperty(object, newValue);
1701: return;
1702: }
1703: }
1704:
1705: throw new MissingFieldException(attribute, theClass);
1706: }
1707:
1708: public ClassNode getClassNode() {
1709: if (classNode == null
1710: && GroovyObject.class.isAssignableFrom(theClass)) {
1711: // lets try load it from the classpath
1712: String className = theClass.getName();
1713: String groovyFile = className;
1714: int idx = groovyFile.indexOf('$');
1715: if (idx > 0) {
1716: groovyFile = groovyFile.substring(0, idx);
1717: }
1718: groovyFile = groovyFile.replace('.', '/') + ".groovy";
1719:
1720: //System.out.println("Attempting to load: " + groovyFile);
1721: URL url = theClass.getClassLoader().getResource(groovyFile);
1722: if (url == null) {
1723: url = Thread.currentThread().getContextClassLoader()
1724: .getResource(groovyFile);
1725: }
1726: if (url != null) {
1727: try {
1728:
1729: /**
1730: * todo there is no CompileUnit in scope so class name
1731: * checking won't work but that mostly affects the bytecode
1732: * generation rather than viewing the AST
1733: */
1734: CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1735: public void call(ClassVisitor writer,
1736: ClassNode node) {
1737: if (node.getName().equals(
1738: theClass.getName())) {
1739: MetaClassImpl.this .classNode = node;
1740: }
1741: }
1742: };
1743:
1744: final ClassLoader parent = theClass
1745: .getClassLoader();
1746: GroovyClassLoader gcl = (GroovyClassLoader) AccessController
1747: .doPrivileged(new PrivilegedAction() {
1748: public Object run() {
1749: return new GroovyClassLoader(parent);
1750: }
1751: });
1752: CompilationUnit unit = new CompilationUnit();
1753: unit.setClassgenCallback(search);
1754: unit.addSource(url);
1755: unit.compile(Phases.CLASS_GENERATION);
1756: } catch (Exception e) {
1757: throw new GroovyRuntimeException(
1758: "Exception thrown parsing: " + groovyFile
1759: + ". Reason: " + e, e);
1760: }
1761: }
1762:
1763: }
1764: return classNode;
1765: }
1766:
1767: public String toString() {
1768: return super .toString() + "[" + theClass + "]";
1769: }
1770:
1771: // Implementation methods
1772: //-------------------------------------------------------------------------
1773:
1774: /**
1775: * Adds all the methods declared in the given class to the metaclass
1776: * ignoring any matching methods already defined by a derived class
1777: *
1778: * @param theClass
1779: */
1780: private void addMethods(final Class theClass) {
1781: Map methodIndex = (Map) classMethodIndex.get(theClass);
1782: if (methodIndex == null) {
1783: methodIndex = new HashMap();
1784: classMethodIndex.put(theClass, methodIndex);
1785: }
1786: // add methods directly declared in the class
1787: Method[] methodArray = (Method[]) AccessController
1788: .doPrivileged(new PrivilegedAction() {
1789: public Object run() {
1790: return theClass.getDeclaredMethods();
1791: }
1792: });
1793: for (int i = 0; i < methodArray.length; i++) {
1794: Method reflectionMethod = methodArray[i];
1795: if (reflectionMethod.getName().indexOf('+') >= 0) {
1796: // Skip Synthetic methods inserted by JDK 1.5 compilers and later
1797: continue;
1798: } else if (Modifier.isAbstract(reflectionMethod
1799: .getModifiers())) {
1800: continue;
1801: }
1802: MetaMethod method = createMetaMethod(reflectionMethod);
1803: addMetaMethod(method);
1804: }
1805: // add methods declared by DGM
1806: List methods = registry.getInstanceMethods();
1807: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1808: Method element = (Method) iter.next();
1809: if (element.getParameterTypes()[0] != theClass)
1810: continue;
1811: addNewInstanceMethod(element);
1812: }
1813: // add static methods declared by DGM
1814: methods = registry.getStaticMethods();
1815: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1816: Method element = (Method) iter.next();
1817: if (element.getParameterTypes()[0] != theClass)
1818: continue;
1819: addNewStaticMethod(element);
1820: }
1821: }
1822:
1823: private void addToClassMethodIndex(MetaMethod method,
1824: Map classMethodIndex) {
1825: Map methodIndex = (Map) classMethodIndex.get(method
1826: .getDeclaringClass());
1827: if (methodIndex == null) {
1828: methodIndex = new HashMap();
1829: classMethodIndex.put(method.getDeclaringClass(),
1830: methodIndex);
1831: }
1832: String name = method.getName();
1833: List list = (List) methodIndex.get(name);
1834: if (list == null) {
1835: list = new ArrayList();
1836: methodIndex.put(name, list);
1837: list.add(method);
1838: } else {
1839: addMethodToList(list, method);
1840: }
1841: }
1842:
1843: /**
1844: * adds a MetaMethod to this class. WARNING: this method will not
1845: * do the neccessary steps for multimethod logic and using this
1846: * method doesn't mean, that a method added here is replacing another
1847: * method from a parent class completely. These steps are usually done
1848: * by initalize, which means if you need these steps, you have to add
1849: * the method before running initialize the first time.
1850: * @see #initialize()
1851: * @param method the MetaMethod
1852: */
1853: protected void addMetaMethod(MetaMethod method) {
1854: if (isInitialized()) {
1855: throw new RuntimeException(
1856: "Already initialized, cannot add new method: "
1857: + method);
1858: }
1859: if (isGenericGetMethod(method) && genericGetMethod == null) {
1860: genericGetMethod = method;
1861: } else if (MetaClassHelper.isGenericSetMethod(method)
1862: && genericSetMethod == null) {
1863: genericSetMethod = method;
1864: }
1865: if (method.isStatic()) {
1866: addToClassMethodIndex(method, classStaticMethodIndex);
1867: }
1868: addToClassMethodIndex(method, classMethodIndex);
1869: }
1870:
1871: protected boolean isInitialized() {
1872: return initialized;
1873: }
1874:
1875: private void addMethodToList(List list, MetaMethod method) {
1876: MetaMethod match = removeMatchingMethod(list, method);
1877: if (match == null) {
1878: list.add(method);
1879: } else if (match.isPrivate()) {
1880: // do not overwrite private methods
1881: // Note: private methods from parent classes are not shown here,
1882: // but when doing the multimethod connection step, we overwrite
1883: // methods of the parent class with methods of a subclass and
1884: // in that case we want to keep the private methods
1885: list.add(match);
1886: } else {
1887: Class methodC = method.getDeclaringClass();
1888: Class matchC = match.getDeclaringClass();
1889: if (methodC == matchC) {
1890: if (method instanceof NewInstanceMetaMethod) {
1891: // let DGM replace existing methods
1892: list.add(method);
1893: } else {
1894: list.add(match);
1895: }
1896: } else if (MetaClassHelper
1897: .isAssignableFrom(methodC, matchC)) {
1898: list.add(match);
1899: } else {
1900: list.add(method);
1901: }
1902: }
1903: }
1904:
1905: /**
1906: * remove a method of the same matching prototype was found in the list
1907: */
1908: private MetaMethod removeMatchingMethod(List list, MetaMethod method) {
1909: for (Iterator iter = list.iterator(); iter.hasNext();) {
1910: MetaMethod aMethod = (MetaMethod) iter.next();
1911: Class[] params1 = aMethod.getParameterTypes();
1912: Class[] params2 = method.getParameterTypes();
1913: if (params1.length == params2.length) {
1914: boolean matches = true;
1915: for (int i = 0; i < params1.length; i++) {
1916: if (params1[i] != params2[i]) {
1917: matches = false;
1918: break;
1919: }
1920: }
1921: if (matches) {
1922: iter.remove();
1923: return (MetaMethod) aMethod;
1924: }
1925: }
1926: }
1927: return null;
1928: }
1929:
1930: /**
1931: * @return the matching method which should be found
1932: */
1933: private MetaMethod findMethod(Method aMethod) {
1934: List methods = getMethods(theClass, aMethod.getName(), false);
1935: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1936: MetaMethod method = (MetaMethod) iter.next();
1937: if (method.isMethod(aMethod)) {
1938: return method;
1939: }
1940: }
1941: //log.warning("Creating reflection based dispatcher for: " + aMethod);
1942: return new ReflectionMetaMethod(aMethod);
1943: }
1944:
1945: /**
1946: * @return the getter method for the given object
1947: */
1948: private MetaMethod findGetter(Object object, String name) {
1949: List methods = getMethods(theClass, name, false);
1950: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1951: MetaMethod method = (MetaMethod) iter.next();
1952: if (method.getParameterTypes().length == 0) {
1953: return method;
1954: }
1955: }
1956: return null;
1957: }
1958:
1959: /**
1960: * @return the Method of the given name with no parameters or null
1961: */
1962: private MetaMethod findStaticGetter(Class type, String name) {
1963: List methods = getStaticMethods(type, name);
1964: for (Iterator iter = methods.iterator(); iter.hasNext();) {
1965: MetaMethod method = (MetaMethod) iter.next();
1966: if (method.getParameterTypes().length == 0) {
1967: return method;
1968: }
1969: }
1970:
1971: /** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1972: try {
1973: Method method = type.getMethod(name,
1974: MetaClassHelper.EMPTY_TYPE_ARRAY);
1975: if ((method.getModifiers() & Modifier.STATIC) != 0) {
1976: return findMethod(method);
1977: } else {
1978: return null;
1979: }
1980: } catch (Exception e) {
1981: return null;
1982: }
1983: }
1984:
1985: private static Object doConstructorInvoke(final Class at,
1986: Constructor constructor, Object[] argumentArray,
1987: boolean setAccessible) {
1988: if (log.isLoggable(Level.FINER)) {
1989: MetaClassHelper.logMethodCall(constructor
1990: .getDeclaringClass(), constructor.getName(),
1991: argumentArray);
1992: }
1993:
1994: if (setAccessible) {
1995: // To fix JIRA 435
1996: // Every constructor should be opened to the accessible classes.
1997: final boolean accessible = MetaClassHelper
1998: .accessibleToConstructor(at, constructor);
1999: final Constructor ctor = constructor;
2000: AccessController.doPrivileged(new PrivilegedAction() {
2001: public Object run() {
2002: ctor.setAccessible(accessible);
2003: return null;
2004: }
2005: });
2006: }
2007: return MetaClassHelper.doConstructorInvoke(constructor,
2008: argumentArray);
2009: }
2010:
2011: /**
2012: * Chooses the correct method to use from a list of methods which match by
2013: * name.
2014: *
2015: * @param methods
2016: * the possible methods to choose from
2017: * @param arguments
2018: * the original argument to the method
2019: */
2020: private Object chooseMethod(String methodName, List methods,
2021: Class[] arguments, boolean coerce) {
2022: int methodCount = methods.size();
2023: if (methodCount <= 0) {
2024: return null;
2025: } else if (methodCount == 1) {
2026: Object method = methods.get(0);
2027: if (MetaClassHelper
2028: .isValidMethod(method, arguments, coerce)) {
2029: return method;
2030: }
2031: return null;
2032: }
2033: Object answer = null;
2034: if (arguments == null || arguments.length == 0) {
2035: answer = MetaClassHelper.chooseEmptyMethodParams(methods);
2036: } else if (arguments.length == 1 && arguments[0] == null) {
2037: answer = MetaClassHelper
2038: .chooseMostGeneralMethodWith1NullParam(methods);
2039: } else {
2040: List matchingMethods = new ArrayList();
2041:
2042: for (Iterator iter = methods.iterator(); iter.hasNext();) {
2043: Object method = iter.next();
2044:
2045: // making this false helps find matches
2046: if (MetaClassHelper.isValidMethod(method, arguments,
2047: coerce)) {
2048: matchingMethods.add(method);
2049: }
2050: }
2051: if (matchingMethods.isEmpty()) {
2052: return null;
2053: } else if (matchingMethods.size() == 1) {
2054: return matchingMethods.get(0);
2055: }
2056: return chooseMostSpecificParams(methodName,
2057: matchingMethods, arguments);
2058:
2059: }
2060: if (answer != null) {
2061: return answer;
2062: }
2063: throw new GroovyRuntimeException(
2064: "Could not find which method to invoke from this list: "
2065: + methods + " for arguments: "
2066: + InvokerHelper.toString(arguments));
2067: }
2068:
2069: private Object chooseMostSpecificParams(String name,
2070: List matchingMethods, Class[] arguments) {
2071:
2072: long matchesDistance = -1;
2073: LinkedList matches = new LinkedList();
2074: for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
2075: Object method = iter.next();
2076: Class[] paramTypes = MetaClassHelper
2077: .getParameterTypes(method);
2078: if (!MetaClassHelper.parametersAreCompatible(arguments,
2079: paramTypes))
2080: continue;
2081: long dist = MetaClassHelper.calculateParameterDistance(
2082: arguments, paramTypes);
2083: if (dist == 0)
2084: return method;
2085: if (matches.size() == 0) {
2086: matches.add(method);
2087: matchesDistance = dist;
2088: } else if (dist < matchesDistance) {
2089: matchesDistance = dist;
2090: matches.clear();
2091: matches.add(method);
2092: } else if (dist == matchesDistance) {
2093: matches.add(method);
2094: }
2095:
2096: }
2097: if (matches.size() == 1) {
2098: return matches.getFirst();
2099: }
2100: if (matches.size() == 0) {
2101: return null;
2102: }
2103:
2104: //more than one matching method found --> ambigous!
2105: String msg = "Ambiguous method overloading for method ";
2106: msg += theClass.getName() + "#" + name;
2107: msg += ".\nCannot resolve which method to invoke for ";
2108: msg += InvokerHelper.toString(arguments);
2109: msg += " due to overlapping prototypes between:";
2110: for (Iterator iter = matches.iterator(); iter.hasNext();) {
2111: Class[] types = MetaClassHelper.getParameterTypes(iter
2112: .next());
2113: msg += "\n\t" + InvokerHelper.toString(types);
2114: }
2115: throw new GroovyRuntimeException(msg);
2116: }
2117:
2118: private boolean isGenericGetMethod(MetaMethod method) {
2119: if (method.getName().equals("get")) {
2120: Class[] parameterTypes = method.getParameterTypes();
2121: return parameterTypes.length == 1
2122: && parameterTypes[0] == String.class;
2123: }
2124: return false;
2125: }
2126:
2127: /**
2128: * Call this method when any mutation method is called, such as adding a new
2129: * method to this MetaClass so that any caching or bytecode generation can be
2130: * regenerated.
2131: */
2132: private synchronized void onMethodChange() {
2133: reflector = null;
2134: }
2135:
2136: public synchronized void initialize() {
2137: if (!isInitialized()) {
2138: fillMethodIndex();
2139: addProperties();
2140: initialized = true;
2141: }
2142: if (reflector == null) {
2143: generateReflector();
2144: }
2145: }
2146:
2147: private void addProperties() {
2148: BeanInfo info;
2149: // introspect
2150: try {
2151: info = (BeanInfo) AccessController
2152: .doPrivileged(new PrivilegedExceptionAction() {
2153: public Object run()
2154: throws IntrospectionException {
2155: return Introspector.getBeanInfo(theClass);
2156: }
2157: });
2158: } catch (PrivilegedActionException pae) {
2159: throw new GroovyRuntimeException(
2160: "exception while bean introspection", pae
2161: .getException());
2162: }
2163: PropertyDescriptor[] descriptors = info
2164: .getPropertyDescriptors();
2165:
2166: // build up the metaproperties based on the public fields, property descriptors,
2167: // and the getters and setters
2168: setupProperties(descriptors);
2169:
2170: EventSetDescriptor[] eventDescriptors = info
2171: .getEventSetDescriptors();
2172: for (int i = 0; i < eventDescriptors.length; i++) {
2173: EventSetDescriptor descriptor = eventDescriptors[i];
2174: Method[] listenerMethods = descriptor.getListenerMethods();
2175: for (int j = 0; j < listenerMethods.length; j++) {
2176: Method listenerMethod = listenerMethods[j];
2177: MetaMethod metaMethod = createMetaMethod(descriptor
2178: .getAddListenerMethod());
2179: String name = listenerMethod.getName();
2180: if (listeners.containsKey(name)) {
2181: listeners.put(name, AMBIGOUS_LISTENER_METHOD);
2182: } else {
2183: listeners.put(name, metaMethod);
2184: }
2185: }
2186: }
2187: }
2188:
2189: private MetaMethod createMetaMethod(final Method method) {
2190: if (registry.useAccessible()) {
2191: AccessController.doPrivileged(new PrivilegedAction() {
2192: public Object run() {
2193: method.setAccessible(true);
2194: return null;
2195: }
2196: });
2197: }
2198:
2199: MetaMethod answer = new MetaMethod(method);
2200: if (isValidReflectorMethod(answer)) {
2201: allMethods.add(answer);
2202: answer.setMethodIndex(allMethods.size());
2203: } else {
2204: //log.warning("Creating reflection based dispatcher for: " + method);
2205: answer = new ReflectionMetaMethod(method);
2206: }
2207:
2208: if (useReflection) {
2209: //log.warning("Creating reflection based dispatcher for: " + method);
2210: return new ReflectionMetaMethod(method);
2211: }
2212:
2213: return answer;
2214: }
2215:
2216: private boolean isValidReflectorMethod(MetaMethod method) {
2217: // We cannot use a reflector if the method is private, protected, or package accessible only.
2218: if (!method.isPublic()) {
2219: return false;
2220: }
2221: // lets see if this method is implemented on an interface
2222: List interfaceMethods = getInterfaceMethods();
2223: for (Iterator iter = interfaceMethods.iterator(); iter
2224: .hasNext();) {
2225: MetaMethod aMethod = (MetaMethod) iter.next();
2226: if (method.isSame(aMethod)) {
2227: method.setInterfaceClass(aMethod.getCallClass());
2228: return true;
2229: }
2230: }
2231: // it's no interface method, so try to find the highest class
2232: // in hierarchy defining this method
2233: Class declaringClass = method.getCallClass();
2234: for (Class clazz = declaringClass; clazz != null; clazz = clazz
2235: .getSuperclass()) {
2236: try {
2237: final Class klazz = clazz;
2238: final String mName = method.getName();
2239: final Class[] parms = method.getParameterTypes();
2240: try {
2241: Method m = (Method) AccessController
2242: .doPrivileged(new PrivilegedExceptionAction() {
2243: public Object run()
2244: throws NoSuchMethodException {
2245: return klazz.getDeclaredMethod(
2246: mName, parms);
2247: }
2248: });
2249: if (!Modifier.isPublic(clazz.getModifiers()))
2250: continue;
2251: if (!Modifier.isPublic(m.getModifiers()))
2252: continue;
2253: declaringClass = clazz;
2254: } catch (PrivilegedActionException pae) {
2255: if (pae.getException() instanceof NoSuchMethodException) {
2256: throw (NoSuchMethodException) pae
2257: .getException();
2258: } else {
2259: throw new RuntimeException(pae.getException());
2260: }
2261: }
2262: } catch (SecurityException e) {
2263: continue;
2264: } catch (NoSuchMethodException e) {
2265: continue;
2266: }
2267: }
2268: if (!Modifier.isPublic(declaringClass.getModifiers()))
2269: return false;
2270: method.setCallClass(declaringClass);
2271:
2272: return true;
2273: }
2274:
2275: private void generateReflector() {
2276: reflector = registry.loadReflector(theClass, allMethods);
2277: if (reflector == null) {
2278: throw new RuntimeException("Should have a reflector for "
2279: + theClass.getName());
2280: }
2281: // lets set the reflector on all the methods
2282: for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
2283: MetaMethod metaMethod = (MetaMethod) iter.next();
2284: metaMethod.setReflector(reflector);
2285: }
2286: }
2287:
2288: public List getMethods() {
2289: return allMethods;
2290: }
2291:
2292: public List getMetaMethods() {
2293: return new ArrayList(newGroovyMethodsList);
2294: }
2295:
2296: private synchronized List getInterfaceMethods() {
2297: if (interfaceMethods == null) {
2298: interfaceMethods = new ArrayList();
2299: Class type = theClass;
2300: while (type != null) {
2301: Class[] interfaces = type.getInterfaces();
2302: for (int i = 0; i < interfaces.length; i++) {
2303: Class iface = interfaces[i];
2304: Method[] methods = iface.getMethods();
2305: addInterfaceMethods(interfaceMethods, methods);
2306: }
2307: type = type.getSuperclass();
2308: }
2309: }
2310: return interfaceMethods;
2311: }
2312:
2313: private void addInterfaceMethods(List list, Method[] methods) {
2314: for (int i = 0; i < methods.length; i++) {
2315: list.add(createMetaMethod(methods[i]));
2316: }
2317: }
2318:
2319: private static class MethodIndexAction {
2320: public void iterate(Map classMethodIndex) {
2321: for (Iterator iter = classMethodIndex.entrySet().iterator(); iter
2322: .hasNext();) {
2323: Map.Entry classEntry = (Map.Entry) iter.next();
2324: Map methodIndex = (Map) classEntry.getValue();
2325: Class clazz = (Class) classEntry.getKey();
2326: if (skipClass(clazz))
2327: continue;
2328: for (Iterator iterator = methodIndex.entrySet()
2329: .iterator(); iterator.hasNext();) {
2330: Map.Entry nameEntry = (Map.Entry) iterator.next();
2331: String name = (String) nameEntry.getKey();
2332: List oldList = (List) nameEntry.getValue();
2333: List newList = methodNameAction(clazz, name,
2334: oldList);
2335: if (replaceMethodList())
2336: nameEntry.setValue(newList);
2337: }
2338: }
2339: }
2340:
2341: public List methodNameAction(Class clazz, String methodName,
2342: List methods) {
2343: List newList = new ArrayList(methods.size());
2344: for (Iterator methodIter = methods.iterator(); methodIter
2345: .hasNext();) {
2346: MetaMethod method = (MetaMethod) methodIter.next();
2347: methodListAction(clazz, methodName, method, methods,
2348: newList);
2349: }
2350: return newList;
2351: }
2352:
2353: public boolean skipClass(Class clazz) {
2354: return false;
2355: }
2356:
2357: public void methodListAction(Class clazz, String methodName,
2358: MetaMethod method, List oldList, List newList) {
2359: }
2360:
2361: public boolean replaceMethodList() {
2362: return true;
2363: }
2364: }
2365:
2366: /**
2367: * @deprecated
2368: */
2369: public Object getProperty(Object object, String property) {
2370: return getProperty(theClass, object, property, false, false);
2371: }
2372:
2373: /**
2374: * @deprecated
2375: */
2376: public void setProperty(Object object, String property,
2377: Object newValue) {
2378: setProperty(theClass, object, property, newValue, false, false);
2379: }
2380:
2381: /**
2382: * @deprecated
2383: */
2384: public Object getAttribute(Object object, String attribute) {
2385: return getAttribute(theClass, object, attribute, false, false);
2386: }
2387:
2388: /**
2389: * @deprecated
2390: */
2391: public void setAttribute(Object object, String attribute,
2392: Object newValue) {
2393: setAttribute(theClass, object, attribute, newValue, false,
2394: false);
2395: }
2396:
2397: public MetaMethod pickMethod(String methodName, Class[] arguments) {
2398: return getMethodWithoutCaching(theClass, methodName, arguments,
2399: false);
2400: }
2401:
2402: /**
2403: * @deprecated use pickMethod instead
2404: */
2405: protected MetaMethod retrieveMethod(String methodName,
2406: Class[] arguments) {
2407: return pickMethod(methodName, arguments);
2408: }
2409:
2410: /**
2411: * remove all method call cache entries. This should be done if a
2412: * method is added during runtime, but not by using a category.
2413: */
2414: protected void clearInvocationCaches() {
2415: staticMethodCache.clear();
2416: methodCache.clear();
2417: }
2418: }
|