1 /*
2 * Copyright 2003-2009 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package groovy.lang;
17
18 import org.codehaus.groovy.GroovyBugError;
19 import org.codehaus.groovy.ast.ClassNode;
20 import org.codehaus.groovy.classgen.BytecodeHelper;
21 import org.codehaus.groovy.control.CompilationUnit;
22 import org.codehaus.groovy.control.Phases;
23 import org.codehaus.groovy.reflection.CachedClass;
24 import org.codehaus.groovy.reflection.CachedConstructor;
25 import org.codehaus.groovy.reflection.CachedField;
26 import org.codehaus.groovy.reflection.CachedMethod;
27 import org.codehaus.groovy.reflection.ClassInfo;
28 import org.codehaus.groovy.reflection.GeneratedMetaMethod;
29 import org.codehaus.groovy.reflection.ParameterTypes;
30 import org.codehaus.groovy.reflection.ReflectionCache;
31 import org.codehaus.groovy.runtime.ConvertedClosure;
32 import org.codehaus.groovy.runtime.CurriedClosure;
33 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
34 import org.codehaus.groovy.runtime.GroovyCategorySupport;
35 import org.codehaus.groovy.runtime.InvokerHelper;
36 import org.codehaus.groovy.runtime.InvokerInvocationException;
37 import org.codehaus.groovy.runtime.MetaClassHelper;
38 import org.codehaus.groovy.runtime.MethodClosure;
39 import org.codehaus.groovy.runtime.callsite.CallSite;
40 import org.codehaus.groovy.runtime.callsite.ConstructorSite;
41 import org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite;
42 import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
43 import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite;
44 import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite;
45 import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite;
46 import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite;
47 import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite;
48 import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
49 import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
50 import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex;
51 import org.codehaus.groovy.runtime.metaclass.MethodSelectionException;
52 import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
53 import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed;
54 import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
55 import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
56 import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
57 import org.codehaus.groovy.runtime.metaclass.NewMetaMethod;
58 import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod;
59 import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod;
60 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
61 import org.codehaus.groovy.runtime.typehandling.NumberMathModificationInfo;
62 import org.codehaus.groovy.runtime.wrappers.Wrapper;
63 import org.codehaus.groovy.util.ComplexKeyHashMap;
64 import org.codehaus.groovy.util.FastArray;
65 import org.codehaus.groovy.util.SingleKeyHashMap;
66 import org.objectweb.asm.ClassVisitor;
67
68 import java.beans.BeanInfo;
69 import java.beans.EventSetDescriptor;
70 import java.beans.IntrospectionException;
71 import java.beans.Introspector;
72 import java.beans.PropertyDescriptor;
73 import java.lang.reflect.Constructor;
74 import java.lang.reflect.Method;
75 import java.lang.reflect.Modifier;
76 import java.lang.reflect.Proxy;
77 import java.net.URL;
78 import java.security.AccessController;
79 import java.security.PrivilegedAction;
80 import java.security.PrivilegedActionException;
81 import java.security.PrivilegedExceptionAction;
82 import java.util.ArrayList;
83 import java.util.Arrays;
84 import java.util.Collection;
85 import java.util.Collections;
86 import java.util.Comparator;
87 import java.util.HashMap;
88 import java.util.HashSet;
89 import java.util.Iterator;
90 import java.util.LinkedList;
91 import java.util.List;
92 import java.util.Map;
93 import java.util.Set;
94
95 /**
96 * Allows methods to be dynamically added to existing classes at runtime
97 *
98 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
99 * @author Guillaume Laforge
100 * @author Jochen Theodorou
101 * @author Graeme Rocher
102 * @author Alex Tkachman
103 * @version $Revision: 17456 $
104 * @see groovy.lang.MetaClass
105 */
106 public class MetaClassImpl implements MetaClass, MutableMetaClass {
107
108 private static final String CLOSURE_CALL_METHOD = "call";
109 private static final String CLOSURE_DO_CALL_METHOD = "doCall";
110 private static final String CLOSURE_CURRY_METHOD = "curry";
111 protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
112 protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
113 protected static final String METHOD_MISSING = "methodMissing";
114 protected static final String PROPERTY_MISSING = "propertyMissing";
115 private static final String GET_PROPERTY_METHOD = "getProperty";
116 private static final String SET_PROPERTY_METHOD = "setProperty";
117 protected static final String INVOKE_METHOD_METHOD = "invokeMethod";
118
119 private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class};
120 private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class};
121 private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
122
123 protected final Class theClass;
124 protected final CachedClass theCachedClass;
125 private static final MetaMethod[] EMPTY = new MetaMethod[0];
126 protected MetaMethod getPropertyMethod;
127 protected MetaMethod invokeMethodMethod;
128 protected MetaMethod setPropertyMethod;
129
130 public final CachedClass getTheCachedClass() {
131 return theCachedClass;
132 }
133
134 protected MetaClassRegistry registry;
135 protected final boolean isGroovyObject;
136 protected final boolean isMap;
137 private ClassNode classNode;
138
139 private final Index classPropertyIndex = new MethodIndex();
140 private Index classPropertyIndexForSuper = new MethodIndex();
141 private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
142
143 private final Map<String, MetaMethod> listeners = new HashMap<String, MetaMethod>();
144 private FastArray constructors;
145 private final List<MetaMethod> allMethods = new ArrayList<MetaMethod>();
146 private boolean initialized;
147 // we only need one of these that can be reused over and over.
148 private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
149 private static final MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod();
150 private static final Object[] EMPTY_ARGUMENTS = {};
151 private final Set<MetaMethod> newGroovyMethodsSet = new HashSet<MetaMethod>();
152
153 private MetaMethod genericGetMethod;
154 private MetaMethod genericSetMethod;
155 private MetaMethod propertyMissingGet;
156 private MetaMethod propertyMissingSet;
157 private MetaMethod methodMissing;
158 private MetaMethodIndex.Header mainClassMethodHeader;
159 protected final MetaMethodIndex metaMethodIndex;
160
161 private final MetaMethod [] myNewMetaMethods;
162 private final MetaMethod [] additionalMetaMethods;
163
164 public MetaClassImpl(final Class theClass, MetaMethod [] add) {
165 this.theClass = theClass;
166 theCachedClass = ReflectionCache.getCachedClass(theClass);
167 this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
168 this.isMap = Map.class.isAssignableFrom(theClass);
169 this.registry = GroovySystem.getMetaClassRegistry();
170 metaMethodIndex = new MetaMethodIndex(theCachedClass);
171 final MetaMethod[] metaMethods = theCachedClass.getNewMetaMethods();
172 if (add != null && !(add.length == 0)) {
173 ArrayList<MetaMethod> arr = new ArrayList<MetaMethod>();
174 arr.addAll(Arrays.asList(metaMethods));
175 arr.addAll(Arrays.asList(add));
176 myNewMetaMethods = arr.toArray(new MetaMethod[arr.size()]);
177 additionalMetaMethods = metaMethods;
178 }
179 else {
180 myNewMetaMethods = metaMethods;
181 additionalMetaMethods = EMPTY;
182 }
183 }
184
185 public MetaClassImpl(final Class theClass) {
186 this(theClass, null);
187 }
188
189 public MetaClassImpl(MetaClassRegistry registry, final Class theClass, MetaMethod add []) {
190 this(theClass, add);
191 this.registry = registry;
192 this.constructors = new FastArray(theCachedClass.getConstructors());
193 }
194
195 public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
196 this(registry, theClass, null);
197 }
198
199 /**
200 * @see MetaObjectProtocol#respondsTo(Object, String, Object[])
201 */
202 public List respondsTo(Object obj, String name, Object[] argTypes) {
203 Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
204 MetaMethod m = getMetaMethod(name, classes);
205 List<MetaMethod> methods = new ArrayList<MetaMethod>();
206 if (m != null) {
207 methods.add(m);
208 }
209 return methods;
210 }
211
212 /**
213 * @see MetaObjectProtocol#respondsTo(Object, String)
214 */
215 public List respondsTo(final Object obj, final String name) {
216 final Object o = getMethods(getTheClass(), name, false);
217 if (o instanceof FastArray)
218 return ((FastArray) o).toList();
219 else
220 return Collections.singletonList(o);
221 }
222
223 /**
224 * @see MetaObjectProtocol#hasProperty(Object,String)
225 */
226 public MetaProperty hasProperty(Object obj, String name) {
227 return getMetaProperty(name);
228 }
229
230 /**
231 * @see MetaObjectProtocol#getMetaProperty(String)
232 */
233 public MetaProperty getMetaProperty(String name) {
234 SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
235 if (propertyMap.containsKey(name)) {
236 return (MetaProperty) propertyMap.get(name);
237 } else if (staticPropertyIndex.containsKey(name)) {
238 return (MetaProperty) staticPropertyIndex.get(name);
239 } else {
240 propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
241 if (propertyMap.containsKey(name))
242 return (MetaProperty) propertyMap.get(name);
243 else {
244 CachedClass superClass = theCachedClass;
245 while (superClass != null && superClass != ReflectionCache.OBJECT_CLASS) {
246 final MetaBeanProperty property = findPropertyInClassHierarchy(name, superClass);
247 if (property != null) {
248 onSuperPropertyFoundInHierarchy(property);
249 return property;
250 }
251 superClass = superClass.getCachedSuperClass();
252 }
253 return null;
254 }
255 }
256 }
257
258 /**
259 * @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
260 */
261 public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) {
262 Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
263 return pickStaticMethod(name, classes);
264 }
265
266
267 /**
268 * @see MetaObjectProtocol#getMetaMethod(String, Object[])
269 */
270 public MetaMethod getMetaMethod(String name, Object[] argTypes) {
271 Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes);
272 return pickMethod(name, classes);
273 }
274
275 public Class getTheClass() {
276 return this.theClass;
277 }
278
279 public boolean isGroovyObject() {
280 return isGroovyObject;
281 }
282
283 private void fillMethodIndex() {
284 mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
285 LinkedList superClasses = getSuperClasses();
286 CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
287
288 Set<CachedClass> interfaces = theCachedClass.getInterfaces();
289 addInterfaceMethods(interfaces);
290
291 populateMethods(superClasses, firstGroovySuper);
292
293 inheritInterfaceNewMetaMethods(interfaces);
294 if (isGroovyObject) {
295 metaMethodIndex.copyMethodsToSuper();
296
297 connectMultimethods(superClasses, firstGroovySuper);
298 removeMultimethodsOverloadedWithPrivateMethods();
299
300 replaceWithMOPCalls(theCachedClass.mopMethods);
301 }
302 }
303
304 private void populateMethods(LinkedList superClasses, CachedClass firstGroovySuper) {
305 Iterator iter = superClasses.iterator();
306
307 MetaMethodIndex.Header header = metaMethodIndex.getHeader(firstGroovySuper.getTheClass());
308 CachedClass c;
309 for (; iter.hasNext();) {
310 c = (CachedClass) iter.next();
311
312 CachedMethod[] cachedMethods = c.getMethods();
313 for (CachedMethod metaMethod : cachedMethods) {
314 addToAllMethodsIfPublic(metaMethod);
315 if (!metaMethod.isPrivate() || c == firstGroovySuper)
316 addMetaMethodToIndex(metaMethod, header);
317 }
318
319 MetaMethod[] cachedMethods1 = getNewMetaMethods(c);
320 for (final MetaMethod method : cachedMethods1) {
321 if (!newGroovyMethodsSet.contains(method)) {
322 newGroovyMethodsSet.add(method);
323 addMetaMethodToIndex(method, header);
324 }
325 }
326
327 if (c == firstGroovySuper)
328 break;
329 }
330
331 MetaMethodIndex.Header last = header;
332 for (;iter.hasNext();) {
333 c = (CachedClass) iter.next();
334 header = metaMethodIndex.getHeader(c.getTheClass());
335
336 if (last != null) {
337 metaMethodIndex.copyNonPrivateMethods(last, header);
338 }
339 last = header;
340
341 for (CachedMethod metaMethod : c.getMethods()) {
342 addToAllMethodsIfPublic(metaMethod);
343 addMetaMethodToIndex(metaMethod, header);
344 }
345
346 for (final MetaMethod method : getNewMetaMethods(c)) {
347 if (!newGroovyMethodsSet.contains(method)) {
348 newGroovyMethodsSet.add(method);
349 addMetaMethodToIndex(method, header);
350 }
351 }
352 }
353 }
354
355 private MetaMethod[] getNewMetaMethods(CachedClass c) {
356 if (theCachedClass != c)
357 return c.getNewMetaMethods();
358
359 return myNewMetaMethods;
360 }
361
362 private void addInterfaceMethods(Set<CachedClass> interfaces) {
363 MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass);
364 for (CachedClass c : interfaces) {
365 final CachedMethod[] m = c.getMethods();
366 for (int i = 0; i != m.length; ++i) {
367 MetaMethod method = m[i];
368 addMetaMethodToIndex(method, header);
369 }
370 }
371 }
372
373 protected LinkedList<CachedClass> getSuperClasses() {
374 LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>();
375
376 if (theClass.isInterface()) {
377 superClasses.addFirst(ReflectionCache.OBJECT_CLASS);
378 } else {
379 for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
380 superClasses.addFirst(c);
381 }
382 if (theCachedClass.isArray && theClass != Object[].class && !theClass.getComponentType().isPrimitive()) {
383 superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS);
384 }
385 }
386 return superClasses;
387 }
388
389 private void removeMultimethodsOverloadedWithPrivateMethods() {
390 MethodIndexAction mia = new MethodIndexAction() {
391 public boolean skipClass(Class clazz) {
392 return clazz == theClass;
393 }
394
395 public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
396 if (e.methods == null)
397 return;
398
399 boolean hasPrivate = false;
400 if (e.methods instanceof FastArray) {
401 FastArray methods = (FastArray) e.methods;
402 final int len = methods.size();
403 final Object[] data = methods.getArray();
404 for (int i = 0; i != len; ++i) {
405 MetaMethod method = (MetaMethod) data[i];
406 if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
407 hasPrivate = true;
408 break;
409 }
410 }
411 }
412 else {
413 MetaMethod method = (MetaMethod) e.methods;
414 if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) {
415 hasPrivate = true;
416 }
417 }
418
419 if (!hasPrivate) return;
420
421 // We have private methods for that name, so remove the
422 // multimethods. That is the same as in our index for
423 // super, so just copy the list from there. It is not
424 // possible to use a pointer here, because the methods
425 // in the index for super are replaced later by MOP
426 // methods like super$5$foo
427 final Object o = e.methodsForSuper;
428 if (o instanceof FastArray)
429 e.methods = ((FastArray) o).copy();
430 else
431 e.methods = o;
432 }
433 };
434 mia.iterate();
435 }
436
437
438 private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
439 // no MOP methods if not a child of GroovyObject
440 if (!isGroovyObject) return;
441
442 class MOPIter extends MethodIndexAction {
443 boolean useThis;
444 public boolean skipClass(CachedClass clazz) {
445 return !useThis && clazz == theCachedClass;
446 }
447
448 public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
449 if (useThis) {
450 if (e.methods == null)
451 return;
452
453 if (e.methods instanceof FastArray) {
454 FastArray methods = (FastArray) e.methods;
455 processFastArray(methods);
456 }
457 else {
458 MetaMethod method = (MetaMethod) e.methods;
459 if (method instanceof NewMetaMethod)
460 return;
461 if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
462 return;
463 String mopName = method.getMopName();
464 int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
465 if (index >= 0) {
466 int from = index;
467 while (from > 0 && mopMethods[from-1].getName().equals(mopName))
468 from--;
469 int to = index;
470 while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
471 to++;
472
473 int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
474 if (matchingMethod != -1) {
475 e.methods = mopMethods[matchingMethod];
476 }
477 }
478 }
479 }
480 else {
481 if (e.methodsForSuper == null)
482 return;
483
484 if (e.methodsForSuper instanceof FastArray) {
485 FastArray methods = (FastArray) e.methodsForSuper;
486 processFastArray(methods);
487 }
488 else {
489 MetaMethod method = (MetaMethod) e.methodsForSuper;
490 if (method instanceof NewMetaMethod)
491 return;
492 if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
493 return;
494 String mopName = method.getMopName();
495 int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
496 if (index >= 0) {
497 int from = index;
498 while (from > 0 && mopMethods[from-1].getName().equals(mopName))
499 from--;
500 int to = index;
501 while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
502 to++;
503
504 int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
505 if (matchingMethod != -1) {
506 e.methodsForSuper = mopMethods[matchingMethod];
507 }
508 }
509 }
510 }
511 }
512
513 private void processFastArray(FastArray methods) {
514 final int len = methods.size();
515 final Object[] data = methods.getArray();
516 for (int i = 0; i != len; ++i) {
517 MetaMethod method = (MetaMethod) data[i];
518 if (method instanceof NewMetaMethod)
519 continue;
520 if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) continue;
521 String mopName = method.getMopName();
522 int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
523 if (index >= 0) {
524 int from = index;
525 while (from > 0 && mopMethods[from-1].getName().equals(mopName))
526 from--;
527 int to = index;
528 while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
529 to++;
530
531 int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
532 if (matchingMethod != -1) {
533 methods.set(i, mopMethods[matchingMethod]);
534 }
535 }
536 }
537 }
538 }
539 MOPIter iter = new MOPIter();
540
541 // replace all calls for super with the correct MOP method
542 iter.useThis = false;
543 iter.iterate();
544 // replace all calls for this with the correct MOP method
545 iter.useThis = true;
546 iter.iterate();
547 }
548
549 private void inheritInterfaceNewMetaMethods(Set<CachedClass> interfaces) {
550 // add methods declared by DGM for interfaces
551 for (CachedClass cls : interfaces) {
552 MetaMethod methods[] = getNewMetaMethods(cls);
553 for (MetaMethod method : methods) {
554 if (!newGroovyMethodsSet.contains(method)) {
555 newGroovyMethodsSet.add(method);
556 }
557 addMetaMethodToIndex(method, mainClassMethodHeader);
558 }
559 }
560 }
561
562 private void connectMultimethods(List superClasses, CachedClass firstGroovyClass) {
563 superClasses = DefaultGroovyMethods.reverse(superClasses);
564 MetaMethodIndex.Header last = null;
565 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
566 CachedClass c = (CachedClass) iter.next();
567 MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getTheClass());
568 // We don't copy DGM methods to superclasses' indexes
569 // The reason we can do that is particular set of DGM methods in use,
570 // if at some point we will define DGM method for some Groovy class or
571 // for a class derived from such, we will need to revise this condition.
572 // It saves us a lot of space and some noticeable time
573 if (last != null) metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex);
574 last = methodIndex;
575
576 if (c == firstGroovyClass)
577 break;
578 }
579 }
580
581 private CachedClass calcFirstGroovySuperClass(Collection superClasses) {
582 if (theCachedClass.isInterface)
583 return ReflectionCache.OBJECT_CLASS;
584
585 CachedClass firstGroovy = null;
586 Iterator iter = superClasses.iterator();
587 for (; iter.hasNext();) {
588 CachedClass c = (CachedClass) iter.next();
589 if (GroovyObject.class.isAssignableFrom(c.getTheClass())) {
590 firstGroovy = c;
591 break;
592 }
593 }
594
595 if (firstGroovy == null)
596 firstGroovy = theCachedClass;
597 else {
598 if (firstGroovy.getTheClass() == GroovyObjectSupport.class && iter.hasNext()) {
599 firstGroovy = (CachedClass) iter.next();
600 if (firstGroovy.getTheClass() == Closure.class && iter.hasNext()) {
601 firstGroovy = (CachedClass) iter.next();
602 }
603 }
604 }
605
606 return GroovyObject.class.isAssignableFrom(firstGroovy.getTheClass()) ? firstGroovy.getCachedSuperClass() : firstGroovy;
607 }
608
609 /**
610 * @return all the normal instance methods avaiable on this class for the
611 * given name
612 */
613 private Object getMethods(Class sender, String name, boolean isCallToSuper) {
614 Object answer;
615
616 final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
617 if (entry == null)
618 answer = FastArray.EMPTY_LIST;
619 else
620 if (isCallToSuper) {
621 answer = entry.methodsForSuper;
622 } else {
623 answer = entry.methods;
624 }
625
626 if (answer == null) answer = FastArray.EMPTY_LIST;
627
628 if (!isCallToSuper) {
629 List used = GroovyCategorySupport.getCategoryMethods(name);
630 if (used != null) {
631 FastArray arr;
632 if (answer instanceof MetaMethod) {
633 arr = new FastArray();
634 arr.add(answer);
635 }
636 else
637 arr = ((FastArray) answer).copy();
638
639 for (Iterator iter = used.iterator(); iter.hasNext();) {
640 MetaMethod element = (MetaMethod) iter.next();
641 if (!element.getDeclaringClass().getTheClass().isAssignableFrom(sender))
642 continue;
643 filterMatchingMethodForCategory(arr, element);
644 }
645 answer = arr;
646 }
647 }
648 return answer;
649 }
650
651 /**
652 * @return all the normal static methods available on this class for the
653 * given name
654 */
655 private Object getStaticMethods(Class sender, String name) {
656 final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
657 if (entry == null)
658 return FastArray.EMPTY_LIST;
659 Object answer = entry.staticMethods;
660 if (answer == null)
661 return FastArray.EMPTY_LIST;
662 return answer;
663 }
664
665 public boolean isModified() {
666 return false; // MetaClassImpl not designed for modification, just return false
667 }
668
669 public void addNewInstanceMethod(Method method) {
670 final CachedMethod cachedMethod = CachedMethod.find(method);
671 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod);
672 final CachedClass declaringClass = newMethod.getDeclaringClass();
673 addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass()));
674 }
675
676 private void addNewInstanceMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
677 if (!newGroovyMethodsSet.contains(newMethod)) {
678 newGroovyMethodsSet.add(newMethod);
679 addMetaMethodToIndex(newMethod, header);
680 }
681 }
682
683 public void addNewStaticMethod(Method method) {
684 final CachedMethod cachedMethod = CachedMethod.find(method);
685 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
686 final CachedClass declaringClass = newMethod.getDeclaringClass();
687 addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass()));
688 }
689
690 private void addNewStaticMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
691 if (!newGroovyMethodsSet.contains(newMethod)) {
692 newGroovyMethodsSet.add(newMethod);
693 addMetaMethodToIndex(newMethod, header);
694 }
695 }
696
697 public Object invokeMethod(Object object, String methodName, Object arguments) {
698 if (arguments == null) {
699 return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
700 }
701 if (arguments instanceof Tuple) {
702 Tuple tuple = (Tuple) arguments;
703 return invokeMethod(object, methodName, tuple.toArray());
704 }
705 if (arguments instanceof Object[]) {
706 return invokeMethod(object, methodName, (Object[]) arguments);
707 } else {
708 return invokeMethod(object, methodName, new Object[]{arguments});
709 }
710 }
711
712 public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
713 return invokeMissingMethod(instance, methodName, arguments, null, false);
714 }
715
716 public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
717 Class theClass = instance instanceof Class ? (Class)instance : instance.getClass();
718 CachedClass superClass = theCachedClass;
719 while(superClass != null && superClass != ReflectionCache.OBJECT_CLASS) {
720 final MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, superClass);
721 if(property != null) {
722 onSuperPropertyFoundInHierarchy(property);
723 if(!isGetter) {
724 property.setProperty(instance, optionalValue);
725 return null;
726 }
727 else {
728 return property.getProperty(instance);
729 }
730 }
731 superClass = superClass.getCachedSuperClass();
732 }
733 // got here to property not found, look for getProperty or setProperty overrides
734 if(isGetter) {
735 final Class[] getPropertyArgs = {String.class};
736 final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD, getPropertyArgs, this);
737 if(method != null && method instanceof ClosureMetaMethod) {
738 onGetPropertyFoundInHierarchy(method);
739 return method.invoke(instance,new Object[]{propertyName});
740 }
741 }
742 else {
743 final Class[] setPropertyArgs = {String.class, Object.class};
744 final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD, setPropertyArgs, this);
745 if(method != null && method instanceof ClosureMetaMethod) {
746 onSetPropertyFoundInHierarchy(method);
747 return method.invoke(instance, new Object[]{propertyName, optionalValue});
748 }
749 }
750
751 try {
752 if (!(instance instanceof Class)) {
753 if (isGetter && propertyMissingGet != null) {
754 return propertyMissingGet.invoke(instance, new Object[]{propertyName});
755 } else {
756 if (propertyMissingSet != null)
757 return propertyMissingSet.invoke(instance, new Object[]{propertyName, optionalValue});
758 }
759 }
760 } catch (InvokerInvocationException iie) {
761 boolean shouldHandle = isGetter && propertyMissingGet != null;
762 if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet != null;
763 if (shouldHandle && iie.getCause() instanceof MissingPropertyException) {
764 throw (MissingPropertyException) iie.getCause();
765 }
766 throw iie;
767 }
768
769 if (instance instanceof Class && theClass != Class.class) {
770 final MetaProperty metaProperty = InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName);
771 if (metaProperty != null)
772 if (isGetter)
773 return metaProperty.getProperty(instance);
774 else {
775 metaProperty.setProperty(instance, optionalValue);
776 return null;
777 }
778 }
779 throw new MissingPropertyExceptionNoStack(propertyName, theClass);
780 }
781
782 private Object invokeMissingMethod(Object instance, String methodName, Object[] arguments, RuntimeException original, boolean isCallToSuper) {
783 if (!isCallToSuper) {
784 Class instanceKlazz = instance.getClass();
785 if (theClass != instanceKlazz && theClass.isAssignableFrom(instanceKlazz))
786 instanceKlazz = theClass;
787
788 Class[] argClasses = MetaClassHelper.castArgumentsToClassArray(arguments);
789
790 MetaMethod method = findMixinMethod(methodName, argClasses);
791 if(method != null) {
792 onMixinMethodFound(method);
793 return method.invoke(instance, arguments);
794 }
795
796 method = findMethodInClassHierarchy(instanceKlazz, methodName, argClasses, this);
797 if(method != null) {
798 onSuperMethodFoundInHierarchy(method);
799 return method.invoke(instance, arguments);
800 }
801
802 // still not method here, so see if there is an invokeMethod method up the hierarchy
803 final Class[] invokeMethodArgs = {String.class, Object[].class};
804 method = findMethodInClassHierarchy(instanceKlazz, INVOKE_METHOD_METHOD, invokeMethodArgs, this );
805 if(method != null && method instanceof ClosureMetaMethod) {
806 onInvokeMethodFoundInHierarchy(method);
807 return method.invoke(instance, invokeMethodArgs);
808 }
809 }
810
811 if (methodMissing != null) {
812 try {
813 return methodMissing.invoke(instance, new Object[]{methodName, arguments});
814 } catch (InvokerInvocationException iie) {
815 if (methodMissing instanceof ClosureMetaMethod && iie.getCause() instanceof MissingMethodException) {
816 MissingMethodException mme = (MissingMethodException) iie.getCause();
817 throw new MissingMethodExecutionFailed (mme.getMethod(), mme.getClass(),
818 mme.getArguments(),mme.isStatic(),mme);
819 }
820 throw iie;
821 }
822 } else if (original != null) throw original;
823 else throw new MissingMethodExceptionNoStack(methodName, theClass, arguments, false);
824 }
825
826 protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) {
827 }
828
829 protected void onMixinMethodFound(MetaMethod method) {
830 }
831
832 protected void onSuperMethodFoundInHierarchy(MetaMethod method) {
833 }
834
835 protected void onInvokeMethodFoundInHierarchy(MetaMethod method) {
836 }
837
838 protected void onSetPropertyFoundInHierarchy(MetaMethod method) {
839 }
840
841 protected void onGetPropertyFoundInHierarchy(MetaMethod method) {
842 }
843
844
845 /**
846 * Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up
847 * "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException
848 *
849 * @param instance The instance
850 * @param propertyName The name of the property
851 * @param optionalValue The value in the case of a setter
852 * @param isGetter True if its a getter
853 * @return The value in the case of a getter or a MissingPropertyException
854 */
855 protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
856 MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
857 if (isGetter) {
858 MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
859 if (propertyMissing != null) {
860 return propertyMissing.invoke(instance, new Object[]{propertyName});
861 }
862 } else {
863 MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
864 if (propertyMissing != null) {
865 return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
866 }
867 }
868
869 if (instance instanceof Class) {
870 throw new MissingPropertyException(propertyName, (Class) instance);
871 }
872 throw new MissingPropertyException(propertyName, theClass);
873 }
874
875 /**
876 * Invokes the given method on the object.
877 * TODO: should this be deprecated? If so, we have to propogate to many places.
878 */
879 public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
880 return invokeMethod(theClass, object, methodName, originalArguments, false, false);
881 }
882
883
884 /**
885 * Invokes the given method on the object.
886 */
887 public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
888 checkInitalised();
889 if (object == null) {
890 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
891 }
892
893 final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
894 // final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
895 //
896 // unwrap(arguments);
897
898 MetaMethod method = getMethodWithCaching(sender, methodName, arguments, isCallToSuper);
899 MetaClassHelper.unwrap(arguments);
900
901 if (method == null)
902 method = tryListParamMetaMethod(sender, methodName, isCallToSuper, arguments);
903
904 final boolean isClosure = object instanceof Closure;
905 if (isClosure) {
906 final Closure closure = (Closure) object;
907
908 final Object owner = closure.getOwner();
909
910 if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) {
911 final Class objectClass = object.getClass();
912 if (objectClass == MethodClosure.class) {
913 final MethodClosure mc = (MethodClosure) object;
914 methodName = mc.getMethod();
915 final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
916 final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
917 return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, arguments, false, false);
918 } else if (objectClass == CurriedClosure.class) {
919 final CurriedClosure cc = (CurriedClosure) object;
920 // change the arguments for an uncurried call
921 final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
922 final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
923 final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
924 return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
925 }
926 if (method==null) invokeMissingMethod(object,methodName,arguments);
927 } else if (CLOSURE_CURRY_METHOD.equals(methodName)) {
928 return closure.curry(arguments);
929 }
930
931 final Object delegate = closure.getDelegate();
932 final boolean isClosureNotOwner = owner != closure;
933 final int resolveStrategy = closure.getResolveStrategy();
934
935 final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
936
937 switch (resolveStrategy) {
938 case Closure.TO_SELF:
939 method = closure.getMetaClass().pickMethod(methodName, argClasses);
940 if (method != null) return method.invoke(closure, arguments);
941 break;
942 case Closure.DELEGATE_ONLY:
943 if (method == null && delegate != closure && delegate != null) {
944 MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
945 method = delegateMetaClass.pickMethod(methodName, argClasses);
946 if (method != null)
947 return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
948 else if (delegate != closure && (delegate instanceof GroovyObject)) {
949 return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
950 }
951 }
952 break;
953 case Closure.OWNER_ONLY:
954 if (method == null && owner != closure) {
955 MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
956 return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
957 }
958 break;
959 case Closure.DELEGATE_FIRST:
960 if (method == null && delegate != closure && delegate != null) {
961 MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
962 method = delegateMetaClass.pickMethod(methodName, argClasses);
963 if (method != null)
964 return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
965 }
966 if (method == null && owner != closure) {
967 MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
968 method = ownerMetaClass.pickMethod(methodName, argClasses);
969 if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
970 }
971 if (method == null && resolveStrategy != Closure.TO_SELF) {
972 // still no methods found, test if delegate or owner are GroovyObjects
973 // and invoke the method on them if so.
974 MissingMethodException last = null;
975 if (delegate != closure && (delegate instanceof GroovyObject)) {
976 try {
977 return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
978 } catch (MissingMethodException mme) {
979 if (last == null) last = mme;
980 }
981 }
982 if (isClosureNotOwner && (owner instanceof GroovyObject)) {
983 try {
984 return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
985 } catch (MissingMethodException mme) {
986 last = mme;
987 }
988 }
989 if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper);
990 }
991
992 break;
993 default:
994 if (method == null && owner != closure) {
995 MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
996 method = ownerMetaClass.pickMethod(methodName, argClasses);
997 if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
998 }
999 if (method == null && delegate != closure && delegate != null) {
1000 MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
1001 method = delegateMetaClass.pickMethod(methodName, argClasses);
1002 if (method != null)
1003 return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
1004 }
1005 if (method == null && resolveStrategy != Closure.TO_SELF) {
1006 // still no methods found, test if delegate or owner are GroovyObjects
1007 // and invoke the method on them if so.
1008 MissingMethodException last = null;
1009 if (isClosureNotOwner && (owner instanceof GroovyObject)) {
1010 try {
1011 return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
1012 } catch (MissingMethodException mme) {
1013 // proboboly needed here, but we need a test case to trip it first
1014 if (last == null) last = mme;
1015 }
1016 catch (InvokerInvocationException iie) {
1017 if (iie.getCause() instanceof MissingMethodException) {
1018 MissingMethodException mme = (MissingMethodException) iie.getCause();
1019 if (methodName.equals(mme.getMethod())) {
1020 if (last == null) last = mme;
1021 } else {
1022 throw iie;
1023 }
1024 }
1025 else
1026 throw iie;
1027 }
1028 }
1029 if (delegate != closure && (delegate instanceof GroovyObject)) {
1030 try {
1031 return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
1032 } catch (MissingMethodException mme) {
1033 last = mme;
1034 }
1035 catch (InvokerInvocationException iie) {
1036 if (iie.getCause() instanceof MissingMethodException) {
1037 last = (MissingMethodException) iie.getCause();
1038 }
1039 else
1040 throw iie;
1041 }
1042 }
1043 if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper);
1044 }
1045 }
1046 }
1047
1048 if (method != null) {
1049 return method.doMethodInvoke(object, arguments);
1050 } else {
1051 return invokePropertyOrMissing(object, methodName, originalArguments, fromInsideClass, isCallToSuper);
1052 }
1053 }
1054
1055 private MetaMethod tryListParamMetaMethod(Class sender, String methodName, boolean isCallToSuper, Object[] arguments) {
1056 MetaMethod method = null;
1057 if (arguments.length == 1 && arguments[0] instanceof List) {
1058 Object[] newArguments = ((List) arguments[0]).toArray();
1059 method = getMethodWithCaching(sender, methodName, newArguments, isCallToSuper);
1060 if (method != null) {
1061 method = new TransformMetaMethod(method) {
1062 public Object invoke(Object object, Object[] arguments) {
1063 Object firstArgument = arguments[0];
1064 List list = (List) firstArgument;
1065 arguments = list.toArray();
1066 return super.invoke(object, arguments);
1067 }
1068 };
1069 }
1070 }
1071 return method;
1072 }
1073
1074 private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
1075 // if no method was found, try to find a closure defined as a field of the class and run it
1076 Object value = null;
1077 final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
1078 if (metaProperty != null)
1079 value = metaProperty.getProperty(object);
1080 else {
1081 if (object instanceof Map)
1082 value = ((Map)object).get(methodName);
1083 }
1084
1085 if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
1086 Closure closure = (Closure) value;
1087 MetaClass delegateMetaClass = closure.getMetaClass();
1088 return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
1089 }
1090
1091 if(object instanceof Script) {
1092 Object bindingVar = ((Script)object).getBinding().getVariables().get(methodName);
1093 if(bindingVar != null) {
1094 MetaClass bindingVarMC = ((MetaClassRegistryImpl)registry).getMetaClass(bindingVar);
1095 return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
1096 }
1097 }
1098 return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
1099 }
1100
1101 private MetaClass lookupObjectMetaClass(Object object) {
1102 if (object instanceof GroovyObject) {
1103 GroovyObject go = (GroovyObject) object;
1104 return go.getMetaClass();
1105 }
1106 Class ownerClass = object.getClass();
1107 if (ownerClass == Class.class) ownerClass = (Class) object;
1108 MetaClass metaClass = registry.getMetaClass(ownerClass);
1109 return metaClass;
1110 }
1111
1112 private Object invokeMethodOnGroovyObject(String methodName, Object[] originalArguments, Object owner) {
1113 GroovyObject go = (GroovyObject) owner;
1114 return go.invokeMethod(methodName, originalArguments);
1115 }
1116
1117 public MetaMethod getMethodWithCaching(Class sender, String methodName, Object[] arguments, boolean isCallToSuper) {
1118 // lets try use the cache to find the method
1119 if (!isCallToSuper && GroovyCategorySupport.hasCategoryInCurrentThread()) {
1120 return getMethodWithoutCaching(sender, methodName, MetaClassHelper.convertToTypeArray(arguments), isCallToSuper);
1121 } else {
1122 final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, methodName);
1123 if (e == null)
1124 return null;
1125
1126 return isCallToSuper ? getSuperMethodWithCaching(arguments, e) : getNormalMethodWithCaching(arguments, e);
1127 }
1128 }
1129
1130 private static boolean sameClasses(Class[] params, Class[] arguments, boolean weakNullCheck) {
1131 // we do here a null check because the params field might not have been
1132 // set yet
1133 if (params==null) return false;
1134
1135 if (params.length != arguments.length)
1136 return false;
1137
1138 for (int i = params.length-1; i >= 0; i--) {
1139 Object arg = arguments[i];
1140 if (arg != null) {
1141 if (params[i] != arguments[i])
1142 return false;
1143 }
1144 else
1145 if (!weakNullCheck)
1146 return false;
1147 }
1148
1149 return true;
1150 }
1151
1152 // This method should be called by CallSite only
1153 private MetaMethod getMethodWithCachingInternal (Class sender, CallSite site, Class [] params) {
1154 if (site.getUsage ().get() != 0 && GroovyCategorySupport.hasCategoryInCurrentThread())
1155 return getMethodWithoutCaching(sender, site.getName (), params, false);
1156
1157 final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, site.getName());
1158 if (e == null) {
1159 return null;
1160 }
1161
1162 MetaMethodIndex.CacheEntry cacheEntry;
1163 final Object methods = e.methods;
1164 if (methods == null)
1165 return null;
1166
1167 cacheEntry = e.cachedMethod;
1168 if (cacheEntry != null
1169 && (sameClasses(cacheEntry.params, params, methods instanceof MetaMethod))) {
1170 return cacheEntry.method;
1171 }
1172
1173 cacheEntry = new MetaMethodIndex.CacheEntry ();
1174 cacheEntry.params = params;
1175 cacheEntry.method = (MetaMethod) chooseMethod(e.name, methods, params);
1176 e.cachedMethod = cacheEntry;
1177 return cacheEntry.method;
1178 }
1179
1180 private MetaMethod getSuperMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) {
1181 MetaMethodIndex.CacheEntry cacheEntry;
1182 if (e.methodsForSuper == null)
1183 return null;
1184
1185 cacheEntry = e.cachedMethodForSuper;
1186
1187 if (cacheEntry != null &&
1188 MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.methodsForSuper instanceof MetaMethod))
1189 {
1190 MetaMethod method = cacheEntry.method;
1191 if (method!=null) return method;
1192 }
1193
1194 cacheEntry = new MetaMethodIndex.CacheEntry ();
1195 final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
1196 cacheEntry.params = classes;
1197 cacheEntry.method = (MetaMethod) chooseMethod(e.name, e.methodsForSuper, classes);
1198 if (cacheEntry.method.isAbstract()) cacheEntry.method = null;
1199
1200 e.cachedMethodForSuper = cacheEntry;
1201
1202 return cacheEntry.method;
1203 }
1204
1205 private MetaMethod getNormalMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) {
1206 MetaMethodIndex.CacheEntry cacheEntry;
1207 final Object methods = e.methods;
1208 if (methods == null)
1209 return null;
1210
1211 cacheEntry = e.cachedMethod;
1212
1213 if (cacheEntry != null &&
1214 MetaClassHelper.sameClasses(cacheEntry.params, arguments, methods instanceof MetaMethod))
1215 {
1216 MetaMethod method = cacheEntry.method;
1217 if (method!=null) return method;
1218 }
1219
1220 cacheEntry = new MetaMethodIndex.CacheEntry ();
1221 final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
1222 cacheEntry.params = classes;
1223 cacheEntry.method = (MetaMethod) chooseMethod(e.name, methods, classes);
1224
1225 e.cachedMethod = cacheEntry;
1226
1227 return cacheEntry.method;
1228 }
1229
1230 public Constructor retrieveConstructor(Class[] arguments) {
1231 CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments);
1232 if (constructor != null) {
1233 return constructor.cachedConstructor;
1234 }
1235 constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments);
1236 if (constructor != null) {
1237 return constructor.cachedConstructor;
1238 }
1239 return null;
1240 }
1241
1242 public MetaMethod retrieveStaticMethod(String methodName, Object[] arguments) {
1243 final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(theClass, methodName);
1244 MetaMethodIndex.CacheEntry cacheEntry;
1245 if (e != null) {
1246 cacheEntry = e.cachedStaticMethod;
1247
1248 if (cacheEntry != null &&
1249 MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.staticMethods instanceof MetaMethod))
1250 {
1251 return cacheEntry.method;
1252 }
1253
1254 cacheEntry = new MetaMethodIndex.CacheEntry ();
1255 final Class[] classes = MetaClassHelper.convertToTypeArray(arguments);
1256 cacheEntry.params = classes;
1257 cacheEntry.method = pickStaticMethod(methodName, classes);
1258
1259 e.cachedStaticMethod = cacheEntry;
1260
1261 return cacheEntry.method;
1262 }
1263 else
1264 return pickStaticMethod(methodName, MetaClassHelper.convertToTypeArray(arguments));
1265 }
1266
1267 public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
1268 MetaMethod method = null;
1269 Object methods = getMethods(sender, methodName, isCallToSuper);
1270 if (methods != null) {
1271 method = (MetaMethod) chooseMethod(methodName, methods, arguments);
1272 }
1273 return method;
1274 }
1275
1276 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
1277 checkInitalised();
1278
1279 final Class sender = object instanceof Class ? (Class) object : object.getClass();
1280 if (sender != theClass) {
1281 MetaClass mc = registry.getMetaClass(sender);
1282 return mc.invokeStaticMethod(sender, methodName, arguments);
1283 }
1284 if (sender == Class.class) {
1285 return invokeMethod(object, methodName, arguments);
1286 }
1287
1288 if (arguments == null) arguments = EMPTY_ARGUMENTS;
1289 // Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
1290
1291 MetaMethod method = retrieveStaticMethod(methodName, arguments);
1292 // lets try use the cache to find the method
1293
1294 if (method != null) {
1295 MetaClassHelper.unwrap(arguments);
1296 return method.doMethodInvoke(object, arguments);
1297 }
1298 Object prop = null;
1299 try {
1300 prop = getProperty(theClass, theClass, methodName, false, false);
1301 } catch (MissingPropertyException mpe) {
1302 // ignore
1303 }
1304
1305 if (prop instanceof Closure) {
1306 return invokeStaticClosureProperty(arguments, prop);
1307 }
1308
1309 Object[] originalArguments = (Object[]) arguments.clone();
1310 MetaClassHelper.unwrap(arguments);
1311
1312 Class superClass = sender.getSuperclass();
1313 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
1314 while (superClass != Object.class && superClass != null) {
1315 MetaClass mc = registry.getMetaClass(superClass);
1316 method = mc.getStaticMetaMethod(methodName, argClasses);
1317 if (method != null) return method.doMethodInvoke(object, arguments);
1318
1319 try {
1320 prop = mc.getProperty(superClass, superClass, methodName, false, false);
1321 } catch (MissingPropertyException mpe) {
1322 // ignore
1323 }
1324
1325 if (prop instanceof Closure) {
1326 return invokeStaticClosureProperty(originalArguments, prop);
1327 }
1328
1329 superClass = superClass.getSuperclass();
1330 }
1331
1332 if(prop != null) {
1333 MetaClass propMC = registry.getMetaClass(prop.getClass());
1334 return propMC.invokeMethod(prop, CLOSURE_CALL_METHOD, arguments);
1335 }
1336
1337 return invokeStaticMissingMethod(sender, methodName, arguments);
1338 }
1339
1340 private Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) {
1341 Closure closure = (Closure) prop;
1342 MetaClass delegateMetaClass = closure.getMetaClass();
1343 return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, false);
1344 }
1345
1346 private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) {
1347 MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, METHOD_MISSING_ARGS);
1348 if (metaMethod != null) {
1349 return metaMethod.invoke(sender, new Object[]{methodName, arguments});
1350 }
1351 throw new MissingMethodException(methodName, sender, arguments, true);
1352 }
1353
1354 private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
1355 MetaMethod method = null;
1356 MethodSelectionException mse = null;
1357 Object methods = getStaticMethods(theClass, methodName);
1358
1359 if (!(methods instanceof FastArray) || !((FastArray)methods).isEmpty()) {
1360 try {
1361 method = (MetaMethod) chooseMethod(methodName, methods, arguments);
1362 } catch(MethodSelectionException msex) {
1363 mse = msex;
1364 }
1365 }
1366 if (method == null && theClass != Class.class) {
1367 MetaClass classMetaClass = registry.getMetaClass(Class.class);
1368 method = classMetaClass.pickMethod(methodName, arguments);
1369 }
1370 if (method == null) {
1371 method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments));
1372 }
1373
1374 if(method == null && mse != null) {
1375 throw mse;
1376 } else {
1377 return method;
1378 }
1379 }
1380
1381 /**
1382 * Warning, this method will be removed
1383 *
1384 * @deprecated use invokeConstructor instead
1385 */
1386 public Object invokeConstructorAt(Class at, Object[] arguments) {
1387 return invokeConstructor(arguments);
1388 }
1389
1390 public Object invokeConstructor(Object[] arguments) {
1391 return invokeConstructor(theClass, arguments);
1392 }
1393
1394 public int selectConstructorAndTransformArguments(int numberOfConstructors, Object[] arguments) {
1395 //TODO: that is just a quick prototype, not the real thing!
1396 if (numberOfConstructors != constructors.size()) {
1397 throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for " +
1398 this.theClass.getName() + " do not match. Expected " + numberOfConstructors + " but got " + constructors.size());
1399 }
1400
1401 if (arguments == null) arguments = EMPTY_ARGUMENTS;
1402 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
1403 MetaClassHelper.unwrap(arguments);
1404 CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
1405 if (constructor == null) {
1406 constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
1407 }
1408 if (constructor == null) {
1409 throw new GroovyRuntimeException(
1410 "Could not find matching constructor for: "
1411 + theClass.getName()
1412 + "(" + InvokerHelper.toTypeString(arguments) + ")");
1413 }
1414 List l = new ArrayList(constructors.toList());
1415 Comparator comp = new Comparator() {
1416 public int compare(Object arg0, Object arg1) {
1417 CachedConstructor c0 = (CachedConstructor) arg0;
1418 CachedConstructor c1 = (CachedConstructor) arg1;
1419 String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes());
1420 String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes());
1421 return descriptor0.compareTo(descriptor1);
1422 }
1423 };
1424 Collections.sort(l, comp);
1425 int found = -1;
1426 for (int i = 0; i < l.size(); i++) {
1427 if (l.get(i) != constructor) continue;
1428 found = i;
1429 break;
1430 }
1431 // NOTE: must be changed to "1 |" if constructor was vargs
1432 return 0 | (found << 8);
1433 }
1434
1435 /**
1436 * checks if the initialisation of the class id complete.
1437 * This method should be called as a form of assert, it is no
1438 * way to test if there is still initialisation work to be done.
1439 * Such logic must be implemented in a different way.
1440 *
1441 * @throws IllegalStateException if the initialisation is incomplete yet
1442 */
1443 protected void checkInitalised() {
1444 if (!isInitialized())
1445 throw new IllegalStateException(
1446 "initialize must be called for meta " +
1447 "class of " + theClass +
1448 "(" + this.getClass() + ") " +
1449 "to complete initialisation process " +
1450 "before any invocation or field/property " +
1451 "access can be done");
1452 }
1453
1454 private Object invokeConstructor(Class at, Object[] arguments) {
1455 checkInitalised();
1456 if (arguments == null) arguments = EMPTY_ARGUMENTS;
1457 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
1458 MetaClassHelper.unwrap(arguments);
1459 CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses);
1460 if (constructor != null) {
1461 return constructor.doConstructorInvoke(arguments);
1462 }
1463
1464 if (arguments.length == 1) {
1465 Object firstArgument = arguments[0];
1466 if (firstArgument instanceof Map) {
1467 constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY);
1468 if (constructor != null) {
1469 Object bean = constructor.doConstructorInvoke(MetaClassHelper.EMPTY_ARRAY);
1470 setProperties(bean, ((Map) firstArgument));
1471 return bean;
1472 }
1473 }
1474 }
1475 throw new GroovyRuntimeException(
1476 "Could not find matching constructor for: "
1477 + theClass.getName()
1478 + "(" + InvokerHelper.toTypeString(arguments) + ")");
1479 }
1480
1481 /**
1482 * Sets a number of bean properties from the given Map where the keys are
1483 * the String names of properties and the values are the values of the
1484 * properties to set
1485 */
1486 public void setProperties(Object bean, Map map) {
1487 checkInitalised();
1488 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
1489 Map.Entry entry = (Map.Entry) iter.next();
1490 String key = entry.getKey().toString();
1491
1492 Object value = entry.getValue();
1493 setProperty(bean, key, value);
1494 }
1495 }
1496
1497 /**
1498 * @return the given property's value on the object
1499 */
1500 public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
1501
1502 //----------------------------------------------------------------------
1503 // handling of static
1504 //----------------------------------------------------------------------
1505 boolean isStatic = theClass != Class.class && object instanceof Class;
1506 if (isStatic && object != theClass) {
1507 MetaClass mc = registry.getMetaClass((Class) object);
1508 return mc.getProperty(sender, object, name, useSuper, false);
1509 }
1510
1511 checkInitalised();
1512
1513 //----------------------------------------------------------------------
1514 // turn getProperty on a Map to get on the Map itself
1515 //----------------------------------------------------------------------
1516 if (!isStatic && this.isMap) {
1517 return ((Map) object).get(name);
1518 }
1519
1520 MetaMethod method = null;
1521 Object[] arguments = EMPTY_ARGUMENTS;
1522
1523 //----------------------------------------------------------------------
1524 // getter
1525 //----------------------------------------------------------------------
1526 MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
1527 if (mp != null) {
1528 if (mp instanceof MetaBeanProperty) {
1529 MetaBeanProperty mbp = (MetaBeanProperty) mp;
1530 method = mbp.getGetter();
1531 mp = mbp.getField();
1532 }
1533 }
1534
1535 // check for a category method named like a getter
1536 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
1537 String getterName = "get" + MetaClassHelper.capitalize(name);
1538 MetaMethod categoryMethod = getCategoryMethodGetter(sender, getterName, false);
1539 if (categoryMethod != null) method = categoryMethod;
1540 }
1541
1542 //----------------------------------------------------------------------
1543 // field
1544 //----------------------------------------------------------------------
1545 if (method == null && mp != null) {
1546 try {
1547 return mp.getProperty(object);
1548 } catch (IllegalArgumentException e) {
1549 // can't access the field directly but there may be a getter
1550 mp = null;
1551 }
1552 }
1553
1554 //----------------------------------------------------------------------
1555 // generic get method
1556 //----------------------------------------------------------------------
1557 // check for a generic get method provided through a category
1558 if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
1559 method = getCategoryMethodGetter(sender, "get", true);
1560 if (method != null) arguments = new Object[]{name};
1561 }
1562
1563 // the generic method is valid, if available (!=null), if static or
1564 // if it is not static and we do no static access
1565 if (method == null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
1566 arguments = new Object[]{name};
1567 method = genericGetMethod;
1568 }
1569
1570 //----------------------------------------------------------------------
1571 // special cases
1572 //----------------------------------------------------------------------
1573 if (method == null) {
1574 /** todo these special cases should be special MetaClasses maybe */
1575 if (theClass != Class.class && object instanceof Class) {
1576 MetaClass mc = registry.getMetaClass(Class.class);
1577 return mc.getProperty(Class.class, object, name, useSuper, false);
1578 } else if (object instanceof Collection) {
1579 return DefaultGroovyMethods.getAt((Collection) object, name);
1580 } else if (object instanceof Object[]) {
1581 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
1582 } else {
1583 MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
1584 if (addListenerMethod != null) {
1585 //TODO: one day we could try return the previously registered Closure listener for easy removal
1586 return null;
1587 }
1588 }
1589 } else {
1590
1591 //----------------------------------------------------------------------
1592 // executing the getter method
1593 //----------------------------------------------------------------------
1594 return method.doMethodInvoke(object, arguments);
1595 }
1596
1597 //----------------------------------------------------------------------
1598 // error due to missing method/field
1599 //----------------------------------------------------------------------
1600 if (isStatic || object instanceof Class)
1601 return invokeStaticMissingProperty(object, name, null, true);
1602 else
1603 return invokeMissingProperty(object, name, null, true);
1604 }
1605
1606 public MetaProperty getEffectiveGetMetaProperty(final Class sender, final Object object, String name, final boolean useSuper) {
1607
1608 //----------------------------------------------------------------------
1609 // handling of static
1610 //----------------------------------------------------------------------
1611 boolean isStatic = theClass != Class.class && object instanceof Class;
1612 if (isStatic && object != theClass) {
1613 return new MetaProperty(name, Object.class) {
1614 final MetaClass mc = registry.getMetaClass((Class) object);
1615
1616 public Object getProperty(Object object) {
1617 return mc.getProperty(sender, object, name, useSuper,false);
1618 }
1619
1620 public void setProperty(Object object, Object newValue) {
1621 throw new UnsupportedOperationException();
1622 }
1623 };
1624 }
1625
1626 checkInitalised();
1627
1628 //----------------------------------------------------------------------
1629 // turn getProperty on a Map to get on the Map itself
1630 //----------------------------------------------------------------------
1631 if (!isStatic && this.isMap) {
1632 return new MetaProperty(name, Object.class) {
1633 public Object getProperty(Object object) {
1634 return ((Map) object).get(name);
1635 }
1636
1637 public void setProperty(Object object, Object newValue) {
1638 throw new UnsupportedOperationException();
1639 }
1640 };
1641 }
1642
1643 MetaMethod method = null;
1644
1645 //----------------------------------------------------------------------
1646 // getter
1647 //----------------------------------------------------------------------
1648 MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
1649 if (mp != null) {
1650 if (mp instanceof MetaBeanProperty) {
1651 MetaBeanProperty mbp = (MetaBeanProperty) mp;
1652 method = mbp.getGetter();
1653 mp = mbp.getField();
1654 }
1655 }
1656
1657 // check for a category method named like a getter
1658 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
1659 String getterName = "get" + MetaClassHelper.capitalize(name);
1660 MetaMethod categoryMethod = getCategoryMethodGetter(sender, getterName, false);
1661 if (categoryMethod != null)
1662 method = categoryMethod;
1663 }
1664
1665 //----------------------------------------------------------------------
1666 // field
1667 //----------------------------------------------------------------------
1668 if (method != null)
1669 return new GetBeanMethodMetaProperty(name, method);
1670
1671 if (mp != null) {
1672 return mp;
1673 // try {
1674 // return mp.getProperty(object);
1675 // } catch (IllegalArgumentException e) {
1676 // // can't access the field directly but there may be a getter
1677 // mp = null;
1678 // }
1679 }
1680
1681 //----------------------------------------------------------------------
1682 // generic get method
1683 //----------------------------------------------------------------------
1684 // check for a generic get method provided through a category
1685 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
1686 method = getCategoryMethodGetter(sender, "get", true);
1687 if (method != null)
1688 return new GetMethodMetaProperty(name, method);
1689 }
1690
1691 // the generic method is valid, if available (!=null), if static or
1692 // if it is not static and we do no static access
1693 if (genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
1694 method = genericGetMethod;
1695 if (method != null)
1696 return new GetMethodMetaProperty(name, method);
1697 }
1698
1699 //----------------------------------------------------------------------
1700 // special cases
1701 //----------------------------------------------------------------------
1702 /** todo these special cases should be special MetaClasses maybe */
1703 if (theClass != Class.class && object instanceof Class) {
1704 return new MetaProperty(name, Object.class) {
1705 public Object getProperty(Object object) {
1706 MetaClass mc = registry.getMetaClass(Class.class);
1707 return mc.getProperty(Class.class, object, name, useSuper, false);
1708 }
1709
1710 public void setProperty(Object object, Object newValue) {
1711 throw new UnsupportedOperationException();
1712 }
1713 };
1714 } else if (object instanceof Collection) {
1715 return new MetaProperty(name, Object.class) {
1716 public Object getProperty(Object object) {
1717 return DefaultGroovyMethods.getAt((Collection) object, name);
1718 }
1719
1720 public void setProperty(Object object, Object newValue) {
1721 throw new UnsupportedOperationException();
1722 }
1723 };
1724 } else if (object instanceof Object[]) {
1725 return new MetaProperty(name, Object.class) {
1726 public Object getProperty(Object object) {
1727 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
1728 }
1729
1730 public void setProperty(Object object, Object newValue) {
1731 throw new UnsupportedOperationException();
1732 }
1733 };
1734 } else {
1735 MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
1736 if (addListenerMethod != null) {
1737 //TODO: one day we could try return the previously registered Closure listener for easy removal
1738 return new MetaProperty(name, Object.class) {
1739 public Object getProperty(Object object) {
1740 return null;
1741 }
1742
1743 public void setProperty(Object object, Object newValue) {
1744 throw new UnsupportedOperationException();
1745 }
1746 };
1747 }
1748 }
1749
1750 //----------------------------------------------------------------------
1751 // error due to missing method/field
1752 //----------------------------------------------------------------------
1753 if (isStatic || object instanceof Class)
1754 return new MetaProperty(name, Object.class) {
1755 public Object getProperty(Object object) {
1756 return invokeStaticMissingProperty(object, name, null, true);
1757 }
1758
1759 public void setProperty(Object object, Object newValue) {
1760 throw new UnsupportedOperationException();
1761 }
1762 };
1763 else
1764 return new MetaProperty(name, Object.class) {
1765 public Object getProperty(Object object) {
1766 return invokeMissingProperty(object, name, null, true);
1767 }
1768
1769 public void setProperty(Object object, Object newValue) {
1770 throw new UnsupportedOperationException();
1771 }
1772 };
1773 }
1774
1775
1776
1777 private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
1778 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(name);
1779 if (possibleGenericMethods != null) {
1780 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
1781 MetaMethod mmethod = (MetaMethod) iter.next();
1782 if (!mmethod.getDeclaringClass().getTheClass().isAssignableFrom(sender))
1783 continue;
1784
1785 CachedClass[] paramTypes = mmethod.getParameterTypes();
1786 if (useLongVersion) {
1787 if (paramTypes.length == 1 && paramTypes[0].getTheClass() == String.class) {
1788 return mmethod;
1789 }
1790 } else {
1791 if (paramTypes.length == 0) return mmethod;
1792 }
1793 }
1794 }
1795 return null;
1796 }
1797
1798 private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
1799 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(name);
1800 if (possibleGenericMethods != null) {
1801 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
1802 MetaMethod mmethod = (MetaMethod) iter.next();
1803 if (!mmethod.getDeclaringClass().getTheClass().isAssignableFrom(sender))
1804 continue;
1805
1806 CachedClass[] paramTypes = mmethod.getParameterTypes();
1807 if (useLongVersion) {
1808 if (paramTypes.length == 2 && paramTypes[0].getTheClass() == String.class) {
1809 return mmethod;
1810 }
1811 } else {
1812 if (paramTypes.length == 1) return mmethod;
1813 }
1814 }
1815 }
1816 return null;
1817 }
1818
1819 /**
1820 * Get all the properties defined for this type
1821 *
1822 * @return a list of MetaProperty objects
1823 */
1824 public List<MetaProperty> getProperties() {
1825 checkInitalised();
1826 SingleKeyHashMap propertyMap = classPropertyIndex.getNullable(theCachedClass);
1827 // simply return the values of the metaproperty map as a List
1828 List ret = new ArrayList(propertyMap.size());
1829 for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
1830 MetaProperty element = (MetaProperty) ((SingleKeyHashMap.Entry) iter.next()).value;
1831 if (element instanceof CachedField) continue;
1832 // filter out DGM beans
1833 if (element instanceof MetaBeanProperty) {
1834 MetaBeanProperty mp = (MetaBeanProperty) element;
1835 boolean setter = true;
1836 boolean getter = true;
1837 if (mp.getGetter() == null || mp.getGetter() instanceof GeneratedMetaMethod || mp.getGetter() instanceof NewInstanceMetaMethod) {
1838 getter = false;
1839 }
1840 if (mp.getSetter() == null || mp.getSetter() instanceof GeneratedMetaMethod || mp.getSetter() instanceof NewInstanceMetaMethod) {
1841 setter = false;
1842 }
1843 if (!setter && !getter) continue;
1844 // TODO: I (ait) don't know why these strange tricks needed and comment following as it effects some Grails tests
1845 // if (!setter && mp.getSetter() != null) {
1846 // element = new MetaBeanProperty(mp.getName(), mp.getType(), mp.getGetter(), null);
1847 // }
1848 // if (!getter && mp.getGetter() != null) {
1849 // element = new MetaBeanProperty(mp.getName(), mp.getType(), null, mp.getSetter());
1850 // }
1851 }
1852 ret.add(element);
1853 }
1854 return ret;
1855 }
1856
1857 private MetaMethod findPropertyMethod(Object methodOrList, boolean isGetter) {
1858 if (methodOrList == null)
1859 return null;
1860
1861 Object ret = null;
1862 if (methodOrList instanceof MetaMethod) {
1863 MetaMethod element = (MetaMethod)methodOrList;
1864 if (!isGetter &&
1865 //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
1866 element.getParameterTypes().length == 1) {
1867 ret = addElementToList(ret, element);
1868 }
1869 if (isGetter &&
1870 !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
1871 element.getParameterTypes().length == 0) {
1872 ret = addElementToList(ret, element);
1873 }
1874
1875 }
1876 else {
1877 FastArray methods = (FastArray) methodOrList;
1878 final int len = methods.size();
1879 final Object[] data = methods.getArray();
1880 for (int i = 0; i != len; ++i) {
1881 MetaMethod element = (MetaMethod) data[i];
1882 if (!isGetter &&
1883 //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
1884 element.getParameterTypes().length == 1) {
1885 ret = addElementToList(ret, element);
1886 }
1887 if (isGetter &&
1888 !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
1889 element.getParameterTypes().length == 0) {
1890 ret = addElementToList(ret, element);
1891 }
1892 }
1893 }
1894
1895 if (ret == null) return null;
1896 if (ret instanceof MetaMethod) return (MetaMethod) ret;
1897
1898 // we found multiple matching methods
1899 // this is a problem, because we can use only one
1900 // if it is a getter, then use the most general return
1901 // type to decide which method to use. If it is a setter
1902 // we use the type of the first parameter
1903 MetaMethod method = null;
1904 int distance = -1;
1905 for (Iterator iter = ((List) ret).iterator(); iter.hasNext();) {
1906 MetaMethod element = (MetaMethod) iter.next();
1907 Class c;
1908 if (isGetter) {
1909 c = element.getReturnType();
1910 } else {
1911 c = element.getParameterTypes()[0].getTheClass();
1912 }
1913 int localDistance = distanceToObject(c);
1914 //TODO: maybe implement the case localDistance==distance
1915 if (distance == -1 || distance > localDistance) {
1916 distance = localDistance;
1917 method = element;
1918 }
1919 }
1920 return method;
1921 }
1922
1923 private Object addElementToList(Object ret, MetaMethod element) {
1924 if (ret == null)
1925 ret = element;
1926 else if (ret instanceof List)
1927 ((List) ret).add(element);
1928 else {
1929 List list = new LinkedList();
1930 list.add(ret);
1931 list.add(element);
1932 ret = list;
1933 }
1934 return ret;
1935 }
1936
1937 private static int distanceToObject(Class c) {
1938 int count;
1939 for (count = 0; c != null; count++) {
1940 c = c.getSuperclass();
1941 }
1942 return count;
1943 }
1944
1945
1946 /**
1947 * This will build up the property map (Map of MetaProperty objects, keyed on
1948 * property name).
1949 */
1950 private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
1951 if (theCachedClass.isInterface) {
1952 LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>();
1953 superClasses.add(ReflectionCache.OBJECT_CLASS);
1954 Set interfaces = theCachedClass.getInterfaces();
1955
1956 classPropertyIndexForSuper = classPropertyIndex;
1957 final SingleKeyHashMap cPI = classPropertyIndex.getNotNull(theCachedClass);
1958 for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
1959 CachedClass iclass = (CachedClass) interfaceIter.next();
1960 SingleKeyHashMap iPropertyIndex = cPI;
1961 addFields(iclass, iPropertyIndex);
1962 classPropertyIndex.put(iclass, iPropertyIndex);
1963 }
1964 classPropertyIndex.put(ReflectionCache.OBJECT_CLASS, cPI);
1965
1966 applyPropertyDescriptors(propertyDescriptors);
1967 applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
1968
1969 makeStaticPropertyIndex();
1970 } else {
1971 LinkedList<CachedClass> superClasses = getSuperClasses();
1972 Set interfaces = theCachedClass.getInterfaces();
1973
1974 // if this an Array, then add the special read-only "length" property
1975 if (theCachedClass.isArray) {
1976 SingleKeyHashMap map = new SingleKeyHashMap();
1977 map.put("length", arrayLengthProperty);
1978 classPropertyIndex.put(theCachedClass, map);
1979 }
1980
1981 inheritStaticInterfaceFields(superClasses, interfaces);
1982 inheritFields(superClasses);
1983
1984 applyPropertyDescriptors(propertyDescriptors);
1985
1986 applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
1987 applyStrayPropertyMethods(superClasses, classPropertyIndexForSuper, false);
1988
1989 copyClassPropertyIndexForSuper(classPropertyIndexForSuper);
1990 makeStaticPropertyIndex();
1991 }
1992 }
1993
1994 private void makeStaticPropertyIndex() {
1995 SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
1996 for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
1997 SingleKeyHashMap.Entry entry = ((SingleKeyHashMap.Entry) iter.next());
1998
1999 MetaProperty mp = (MetaProperty) entry.getValue();
2000 if (mp instanceof CachedField) {
2001 CachedField mfp = (CachedField) mp;
2002 if (!mfp.isStatic()) continue;
2003 } else if (mp instanceof MetaBeanProperty) {
2004 MetaProperty result = establishStaticMetaProperty(mp);
2005 if (result == null) continue;
2006 else {
2007 mp = result;
2008 }
2009 } else {
2010 continue; // ignore all other types
2011 }
2012 staticPropertyIndex.put(entry.getKey(), mp);
2013 }
2014
2015 }
2016
2017 private MetaProperty establishStaticMetaProperty(MetaProperty mp) {
2018 MetaBeanProperty mbp = (MetaBeanProperty) mp;
2019 MetaProperty result = null;
2020 final MetaMethod getterMethod = mbp.getGetter();
2021 final MetaMethod setterMethod = mbp.getSetter();
2022 final CachedField metaField = mbp.getField();
2023
2024 boolean getter = getterMethod == null || getterMethod.isStatic();
2025 boolean setter = setterMethod == null || setterMethod.isStatic();
2026 boolean field = metaField == null || metaField.isStatic();
2027
2028 if (!getter && !setter && !field) {
2029 return result;
2030 } else {
2031 final String propertyName = mbp.getName();
2032 final Class propertyType = mbp.getType();
2033
2034 if (setter && getter) {
2035 if (field) {
2036 result = mbp; // nothing to do
2037 } else {
2038 result = new MetaBeanProperty(propertyName, propertyType, getterMethod, setterMethod);
2039 }
2040 } else if (getter && !setter) {
2041 if (getterMethod == null) {
2042 result = metaField;
2043 } else {
2044 MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, getterMethod, null);
2045 if (field) newmp.setField(metaField);
2046 result = newmp;
2047 }
2048 } else if (setter && !getter) {
2049 if (setterMethod == null) {
2050 result = metaField;
2051 } else {
2052 MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, null, setterMethod);
2053 if (field) newmp.setField(metaField);
2054 result = newmp;
2055 }
2056 } else
2057 result = metaField;
2058 }
2059 return result;
2060 }
2061
2062 private void copyClassPropertyIndexForSuper(Index dest) {
2063 for (ComplexKeyHashMap.EntryIterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext();) {
2064 SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
2065 SingleKeyHashMap newVal = new SingleKeyHashMap();
2066 dest.put((CachedClass) entry.getKey(), newVal);
2067 }
2068 }
2069
2070 private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
2071 for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
2072 CachedClass iclass = (CachedClass) interfaceIter.next();
2073 SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(iclass);
2074 addFields(iclass, iPropertyIndex);
2075 for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
2076 CachedClass sclass = (CachedClass) classIter.next();
2077 if (!iclass.getTheClass().isAssignableFrom(sclass.getTheClass())) continue;
2078 SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(sclass);
2079 copyNonPrivateFields(iPropertyIndex, sPropertyIndex);
2080 }
2081 }
2082 }
2083
2084 private void inheritFields(LinkedList<CachedClass> superClasses) {
2085 SingleKeyHashMap last = null;
2086 for (CachedClass klass : superClasses) {
2087 SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
2088 if (last != null) {
2089 copyNonPrivateFields(last, propertyIndex);
2090 }
2091 last = propertyIndex;
2092 addFields(klass, propertyIndex);
2093 }
2094 }
2095
2096 private void addFields(final CachedClass klass, SingleKeyHashMap propertyIndex) {
2097 CachedField[] fields = klass.getFields();
2098 for (CachedField field : fields) {
2099 propertyIndex.put(field.getName(), field);
2100 }
2101 }
2102
2103 private void copyNonPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to) {
2104 for (ComplexKeyHashMap.EntryIterator iter = from.getEntrySetIterator(); iter.hasNext();) {
2105 SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
2106 CachedField mfp = (CachedField) entry.getValue();
2107 if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
2108 to.put(entry.getKey(), mfp);
2109 }
2110 }
2111
2112 private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Index classPropertyIndex, boolean isThis) {
2113 // now look for any stray getters that may be used to define a property
2114 for (CachedClass klass : superClasses) {
2115 MetaMethodIndex.Header header = metaMethodIndex.getHeader(klass.getTheClass());
2116 SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
2117 for (MetaMethodIndex.Entry e = header.head; e != null; e = e.nextClassEntry) {
2118 String methodName = e.name;
2119 // name too short?
2120 if (methodName.length() < 4) continue;
2121 // possible getter/setter?
2122 boolean isGetter = methodName.startsWith("get");
2123 boolean isSetter = methodName.startsWith("set");
2124 if (!isGetter && !isSetter) continue;
2125
2126 MetaMethod propertyMethod = findPropertyMethod(isThis ? e.methods : e.methodsForSuper, isGetter);
2127 if (propertyMethod == null) continue;
2128
2129 String propName = getPropName(methodName);
2130 createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
2131 }
2132 }
2133 }
2134
2135 private static final HashMap<String, String> propNames = new HashMap<String, String>(1024);
2136
2137 private String getPropName(String methodName) {
2138 String name = propNames.get(methodName);
2139 if (name != null)
2140 return name;
2141
2142 synchronized (propNames) {
2143 String propName = java.beans.Introspector.decapitalize(methodName.substring(3));
2144 propNames.put(methodName, propName);
2145 return propName;
2146 }
2147 }
2148
2149 private void createMetaBeanProperty(SingleKeyHashMap propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
2150 // is this property already accounted for?
2151 MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
2152 if (mp == null) {
2153 if (isGetter) {
2154 mp = new MetaBeanProperty(propName,
2155 propertyMethod.getReturnType(),
2156 propertyMethod, null);
2157 } else {
2158 //isSetter
2159 mp = new MetaBeanProperty(propName,
2160 propertyMethod.getParameterTypes()[0].getTheClass(),
2161 null, propertyMethod);
2162 }
2163 } else {
2164 MetaBeanProperty mbp;
2165 CachedField mfp;
2166 if (mp instanceof MetaBeanProperty) {
2167 mbp = (MetaBeanProperty) mp;
2168 mfp = mbp.getField();
2169 } else if (mp instanceof CachedField) {
2170 mfp = (CachedField) mp;
2171 mbp = new MetaBeanProperty(propName,
2172 mfp.getType(),
2173 null, null);
2174 } else {
2175 throw new GroovyBugError("unknown MetaProperty class used. Class is " + mp.getClass());
2176 }
2177 // we may have already found one for this name
2178 if (isGetter && mbp.getGetter() == null) {
2179 mbp.setGetter(propertyMethod);
2180 } else if (!isGetter && mbp.getSetter() == null) {
2181 mbp.setSetter(propertyMethod);
2182 }
2183 mbp.setField(mfp);
2184 mp = mbp;
2185 }
2186 propertyIndex.put(propName, mp);
2187 }
2188
2189 private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
2190 // now iterate over the map of property descriptors and generate
2191 // MetaBeanProperty objects
2192 for (PropertyDescriptor pd : propertyDescriptors) {
2193 // skip if the property type is unknown (this seems to be the case if the
2194 // property descriptor is based on a setX() method that has two parameters,
2195 // which is not a valid property)
2196 if (pd.getPropertyType() == null)
2197 continue;
2198
2199 // get the getter method
2200 Method method = pd.getReadMethod();
2201 MetaMethod getter;
2202
2203 if (method != null) {
2204 CachedMethod cachedGetter = CachedMethod.find(method);
2205 getter = cachedGetter == null ? null : findMethod(cachedGetter);
2206 } else {
2207 getter = null;
2208 }
2209
2210 // get the setter method
2211 MetaMethod setter;
2212 method = pd.getWriteMethod();
2213 if (method != null) {
2214 CachedMethod cachedSetter = CachedMethod.find(method);
2215 setter = cachedSetter == null ? null : findMethod(cachedSetter);
2216 } else {
2217 setter = null;
2218 }
2219
2220 // now create the MetaProperty object
2221 MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
2222 addMetaBeanProperty(mp);
2223 }
2224 }
2225
2226 /**
2227 * Adds a new MetaBeanProperty to this MetaClass
2228 *
2229 * @param mp The MetaBeanProperty
2230 */
2231 public void addMetaBeanProperty(MetaBeanProperty mp) {
2232
2233 MetaProperty staticProperty = establishStaticMetaProperty(mp);
2234 if (staticProperty != null) {
2235 staticPropertyIndex.put(mp.getName(), mp);
2236 } else {
2237
2238 SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
2239 //keep field
2240 CachedField field;
2241 MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
2242 if (old != null) {
2243 if (old instanceof MetaBeanProperty) {
2244 field = ((MetaBeanProperty) old).getField();
2245 } else {
2246 field = (CachedField) old;
2247 }
2248 mp.setField(field);
2249 }
2250
2251 // put it in the list
2252 // this will overwrite a possible field property
2253 propertyMap.put(mp.getName(), mp);
2254 }
2255
2256 }
2257
2258 /**
2259 * Sets the property value on an object
2260 */
2261 public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
2262 checkInitalised();
2263
2264 //----------------------------------------------------------------------
2265 // handling of static
2266 //----------------------------------------------------------------------
2267 boolean isStatic = theClass != Class.class && object instanceof Class;
2268 if (isStatic && object != theClass) {
2269 MetaClass mc = registry.getMetaClass((Class) object);
2270 mc.getProperty(sender, object, name, useSuper, fromInsideClass);
2271 return;
2272 }
2273
2274 //----------------------------------------------------------------------
2275 // Unwrap wrapped values fo now - the new MOP will handle them properly
2276 //----------------------------------------------------------------------
2277 if (newValue instanceof Wrapper) newValue = ((Wrapper) newValue).unwrap();
2278
2279 MetaMethod method = null;
2280 Object[] arguments = null;
2281
2282 //----------------------------------------------------------------------
2283 // setter
2284 //----------------------------------------------------------------------
2285 MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
2286 MetaProperty field = null;
2287 if (mp != null) {
2288 if (mp instanceof MetaBeanProperty) {
2289 MetaBeanProperty mbp = (MetaBeanProperty) mp;
2290 method = mbp.getSetter();
2291 MetaProperty f = mbp.getField();
2292 if (method != null || (f != null && !Modifier.isFinal(f.getModifiers()))) {
2293 arguments = new Object[]{newValue};
2294 field = f;
2295 }
2296 } else {
2297 field = mp;
2298 }
2299 }
2300
2301 // check for a category method named like a setter
2302 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
2303 String getterName = "set" + MetaClassHelper.capitalize(name);
2304 MetaMethod categoryMethod = getCategoryMethodSetter(sender, getterName, false);
2305 if (categoryMethod != null) {
2306 method = categoryMethod;
2307 arguments = new Object[]{newValue};
2308 }
2309 }
2310
2311 //----------------------------------------------------------------------
2312 // listener method
2313 //----------------------------------------------------------------------
2314 boolean ambiguousListener = false;
2315 if (method == null) {
2316 method = (MetaMethod) listeners.get(name);
2317 ambiguousListener = method == AMBIGUOUS_LISTENER_METHOD;
2318 if (method != null &&
2319 !ambiguousListener &&
2320 newValue instanceof Closure) {
2321 // lets create a dynamic proxy
2322 Object proxy = Proxy.newProxyInstance(
2323 theClass.getClassLoader(),
2324 new Class[]{method.getParameterTypes()[0].getTheClass()},
2325 new ConvertedClosure((Closure) newValue, name));
2326 arguments = new Object[]{proxy};
2327 newValue = proxy;
2328 } else {
2329 method = null;
2330 }
2331 }
2332
2333 //----------------------------------------------------------------------
2334 // field
2335 //----------------------------------------------------------------------
2336 if (method == null && field != null) {
2337 if (Modifier.isFinal(field.getModifiers())) {
2338 throw new ReadOnlyPropertyException(name, theClass);
2339 }
2340 if(!(this.isMap && isPrivateOrPkgPrivate(field.getModifiers()))) {
2341 field.setProperty(object, newValue);
2342 return;
2343 }
2344 }
2345
2346 //----------------------------------------------------------------------
2347 // generic set method
2348 //----------------------------------------------------------------------
2349 // check for a generic get method provided through a category
2350 if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
2351 method = getCategoryMethodSetter(sender, "set", true);
2352 if (method != null) arguments = new Object[]{name, newValue};
2353 }
2354
2355 // the generic method is valid, if available (!=null), if static or
2356 // if it is not static and we do no static access
2357 if (method == null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
2358 arguments = new Object[]{name, newValue};
2359 method = genericSetMethod;
2360 }
2361
2362 //----------------------------------------------------------------------
2363 // executing the getter method
2364 //----------------------------------------------------------------------
2365 if (method != null) {
2366 if (arguments.length == 1) {
2367 newValue = DefaultTypeTransformation.castToType(
2368 newValue,
2369 method.getParameterTypes()[0].getTheClass());
2370 arguments[0] = newValue;
2371 } else {
2372 newValue = DefaultTypeTransformation.castToType(
2373 newValue,
2374 method.getParameterTypes()[1].getTheClass());
2375 arguments[1] = newValue;
2376 }
2377 method.doMethodInvoke(object, arguments);
2378 return;
2379 }
2380
2381 //----------------------------------------------------------------------
2382 // turn setProperty on a Map to put on the Map itself
2383 //----------------------------------------------------------------------
2384 if (!isStatic && this.isMap) {
2385 ((Map) object).put(name, newValue);
2386 return;
2387 }
2388
2389
2390 //----------------------------------------------------------------------
2391 // error due to missing method/field
2392 //----------------------------------------------------------------------
2393 if (ambiguousListener) {
2394 throw new GroovyRuntimeException("There are multiple listeners for the property " + name + ". Please do not use the bean short form to access this listener.");
2395 }
2396 if (mp != null) {
2397 throw new ReadOnlyPropertyException(name, theClass);
2398 }
2399
2400 invokeMissingProperty(object, name, newValue, false);
2401 }
2402
2403 private boolean isPrivateOrPkgPrivate(int mod) {
2404 return !Modifier.isProtected(mod) && !Modifier.isPublic(mod);
2405 }
2406
2407 private MetaProperty getMetaProperty(Class _clazz, String name, boolean useSuper, boolean useStatic) {
2408 if (_clazz == theClass)
2409 return getMetaProperty(name, useStatic);
2410
2411 CachedClass clazz = ReflectionCache.getCachedClass(_clazz);
2412 while (true) {
2413 SingleKeyHashMap propertyMap;
2414 if (useStatic) {
2415 propertyMap = staticPropertyIndex;
2416 } else if (useSuper) {
2417 propertyMap = classPropertyIndexForSuper.getNullable(clazz);
2418 } else {
2419 propertyMap = classPropertyIndex.getNullable(clazz);
2420 }
2421 if (propertyMap == null) {
2422 if (clazz != theCachedClass) {
2423 clazz = theCachedClass;
2424 continue;
2425 } else {
2426 return null;
2427 }
2428 }
2429 return (MetaProperty) propertyMap.get(name);
2430 }
2431 }
2432
2433
2434 private MetaProperty getMetaProperty(String name, boolean useStatic) {
2435 CachedClass clazz = theCachedClass;
2436 SingleKeyHashMap propertyMap;
2437 if (useStatic) {
2438 propertyMap = staticPropertyIndex;
2439 } else {
2440 propertyMap = classPropertyIndex.getNullable(clazz);
2441 }
2442 if (propertyMap == null) {
2443 return null;
2444 }
2445 return (MetaProperty) propertyMap.get(name);
2446 }
2447
2448
2449 public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
2450 return getAttribute(receiver, messageName);
2451 }
2452
2453 /**
2454 * Looks up the given attribute (field) on the given object
2455 */
2456 public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
2457 checkInitalised();
2458
2459 boolean isStatic = theClass != Class.class && object instanceof Class;
2460 if (isStatic && object != theClass) {
2461 MetaClass mc = registry.getMetaClass((Class) object);
2462 return mc.getAttribute(sender, object, attribute, useSuper);
2463 }
2464
2465 MetaProperty mp = getMetaProperty(sender, attribute, useSuper, isStatic);
2466
2467 if (mp != null) {
2468 if (mp instanceof MetaBeanProperty) {
2469 MetaBeanProperty mbp = (MetaBeanProperty) mp;
2470 mp = mbp.getField();
2471 }
2472 try {
2473 // delegate the get operation to the metaproperty
2474 if (mp != null) return mp.getProperty(object);
2475 } catch (Exception e) {
2476 throw new GroovyRuntimeException("Cannot read field: " + attribute, e);
2477 }
2478 }
2479
2480 throw new MissingFieldException(attribute, theClass);
2481 }
2482
2483 /**
2484 * Sets the given attribute (field) on the given object
2485 */
2486 public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
2487 checkInitalised();
2488
2489 boolean isStatic = theClass != Class.class && object instanceof Class;
2490 if (isStatic && object != theClass) {
2491 MetaClass mc = registry.getMetaClass((Class) object);
2492 mc.setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);
2493 return;
2494 }
2495
2496 MetaProperty mp = getMetaProperty(sender, attribute, useSuper, isStatic);
2497
2498 if (mp != null) {
2499 if (mp instanceof MetaBeanProperty) {
2500 MetaBeanProperty mbp = (MetaBeanProperty) mp;
2501 mp = mbp.getField();
2502 }
2503 if (mp != null) {
2504 mp.setProperty(object, newValue);
2505 return;
2506 }
2507 }
2508
2509 throw new MissingFieldException(attribute, theClass);
2510 }
2511
2512 public ClassNode getClassNode() {
2513 if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
2514 // let's try load it from the classpath
2515 String groovyFile = theClass.getName();
2516 int idx = groovyFile.indexOf('$');
2517 if (idx > 0) {
2518 groovyFile = groovyFile.substring(0, idx);
2519 }
2520 groovyFile = groovyFile.replace('.', '/') + ".groovy";
2521
2522 //System.out.println("Attempting to load: " + groovyFile);
2523 URL url = theClass.getClassLoader().getResource(groovyFile);
2524 if (url == null) {
2525 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
2526 }
2527 if (url != null) {
2528 try {
2529
2530 /**
2531 * todo there is no CompileUnit in scope so class name
2532 * checking won't work but that mostly affects the bytecode
2533 * generation rather than viewing the AST
2534 */
2535 CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
2536 public void call(ClassVisitor writer, ClassNode node) {
2537 if (node.getName().equals(theClass.getName())) {
2538 MetaClassImpl.this.classNode = node;
2539 }
2540 }
2541 };
2542
2543 final ClassLoader parent = theClass.getClassLoader();
2544 GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
2545 public Object run() {
2546 return new GroovyClassLoader(parent);
2547 }
2548 });
2549 CompilationUnit unit = new CompilationUnit();
2550 unit.setClassgenCallback(search);
2551 unit.addSource(url);
2552 unit.compile(Phases.CLASS_GENERATION);
2553 }
2554 catch (Exception e) {
2555 throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
2556 }
2557 }
2558
2559 }
2560 return classNode;
2561 }
2562
2563 public String toString() {
2564 return super.toString() + "[" + theClass + "]";
2565 }
2566
2567 // Implementation methods
2568 //-------------------------------------------------------------------------
2569
2570
2571 /**
2572 * adds a MetaMethod to this class. WARNING: this method will not
2573 * do the neccessary steps for multimethod logic and using this
2574 * method doesn't mean, that a method added here is replacing another
2575 * method from a parent class completely. These steps are usually done
2576 * by initalize, which means if you need these steps, you have to add
2577 * the method before running initialize the first time.
2578 *
2579 * @param method the MetaMethod
2580 * @see #initialize()
2581 */
2582 public void addMetaMethod(MetaMethod method) {
2583 if (isInitialized()) {
2584 throw new RuntimeException("Already initialized, cannot add new method: " + method);
2585 }
2586
2587 final CachedClass declaringClass = method.getDeclaringClass();
2588 addMetaMethodToIndex(method, metaMethodIndex.getHeader(declaringClass.getTheClass()));
2589 }
2590
2591 protected void addMetaMethodToIndex(MetaMethod method, MetaMethodIndex.Header header) {
2592 checkIfStdMethod(method);
2593
2594 String name = method.getName();
2595 MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(name, header);
2596 if (method.isStatic()) {
2597 e.staticMethods = metaMethodIndex.addMethodToList(e.staticMethods, method);
2598 }
2599 e.methods = metaMethodIndex.addMethodToList(e.methods, method);
2600 }
2601
2602 /**
2603 * Checks if the metaMethod is a method from the GroovyObject interface such as setProperty, getProperty and invokeMethod
2604 *
2605 * @param metaMethod The metaMethod instance
2606 * @see GroovyObject
2607 */
2608 protected final void checkIfGroovyObjectMethod(MetaMethod metaMethod) {
2609 if (metaMethod instanceof ClosureMetaMethod || metaMethod instanceof MixinInstanceMetaMethod) {
2610 if(isGetPropertyMethod(metaMethod)) {
2611 getPropertyMethod = metaMethod;
2612 }
2613 else if(isInvokeMethod(metaMethod)) {
2614 invokeMethodMethod = metaMethod;
2615 }
2616 else if(isSetPropertyMethod(metaMethod)) {
2617 setPropertyMethod = metaMethod;
2618 }
2619 }
2620 }
2621
2622 private boolean isSetPropertyMethod(MetaMethod metaMethod) {
2623 return SET_PROPERTY_METHOD.equals(metaMethod.getName()) && metaMethod.getParameterTypes().length == 2;
2624 }
2625
2626 private boolean isGetPropertyMethod(MetaMethod metaMethod) {
2627 return GET_PROPERTY_METHOD.equals(metaMethod.getName());
2628 }
2629
2630 private boolean isInvokeMethod(MetaMethod metaMethod) {
2631 return INVOKE_METHOD_METHOD.equals(metaMethod.getName()) && metaMethod.getParameterTypes().length == 2;
2632 }
2633
2634 private void checkIfStdMethod(MetaMethod method) {
2635 checkIfGroovyObjectMethod(method);
2636
2637 if (isGenericGetMethod(method) && genericGetMethod == null) {
2638 genericGetMethod = method;
2639 } else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
2640 genericSetMethod = method;
2641 }
2642 if (propertyMissingGet == null && method.getName().equals(PROPERTY_MISSING)) {
2643 CachedClass[] parameterTypes = method.getParameterTypes();
2644 if (parameterTypes.length == 1) {
2645 propertyMissingGet = method;
2646 }
2647 }
2648 if (propertyMissingSet == null && method.getName().equals(PROPERTY_MISSING)) {
2649 CachedClass[] parameterTypes = method.getParameterTypes();
2650 if (parameterTypes.length == 2) {
2651 propertyMissingSet = method;
2652 }
2653 }
2654 if (method.getName().equals(METHOD_MISSING)) {
2655 CachedClass[] parameterTypes = method.getParameterTypes();
2656 if (parameterTypes.length == 2
2657 && parameterTypes[0].getTheClass() == String.class
2658 && parameterTypes[1].getTheClass() == Object.class) {
2659 methodMissing = method;
2660 }
2661 }
2662
2663 if (theCachedClass.isNumber) {
2664 NumberMathModificationInfo.instance.checkIfStdMethod (method);
2665 }
2666 }
2667
2668 protected boolean isInitialized() {
2669 return initialized;
2670 }
2671
2672 /**
2673 * return false: add method
2674 * null: ignore method
2675 * true: replace
2676 */
2677 private Boolean getMatchKindForCategory(MetaMethod aMethod, MetaMethod categoryMethod) {
2678 CachedClass[] params1 = aMethod.getParameterTypes();
2679 CachedClass[] params2 = categoryMethod.getParameterTypes();
2680 if (params1.length != params2.length) return Boolean.FALSE;
2681
2682 for (int i = 0; i < params1.length; i++) {
2683 if (params1[i] != params2[i]) return Boolean.FALSE;
2684 }
2685
2686 Class aMethodClass = aMethod.getDeclaringClass().getTheClass();
2687 Class categoryMethodClass = categoryMethod.getDeclaringClass().getTheClass();
2688
2689 if (aMethodClass==categoryMethodClass) return Boolean.TRUE;
2690 boolean match = aMethodClass.isAssignableFrom(categoryMethodClass);
2691 if (match) return Boolean.TRUE;
2692 return null;
2693 }
2694
2695 private void filterMatchingMethodForCategory(FastArray list, MetaMethod method) {
2696 int len = list.size();
2697 if (len==0) {
2698 list.add(method);
2699 return;
2700 }
2701
2702 Object data[] = list.getArray();
2703 for (int j = 0; j != len; ++j) {
2704 MetaMethod aMethod = (MetaMethod) data[j];
2705 Boolean match = getMatchKindForCategory(aMethod, method);
2706 // true == replace
2707 if (match==Boolean.TRUE) {
2708 list.set(j, method);
2709 return;
2710 // null == ignore (we have a better method already)
2711 } else if (match==null) {
2712 return;
2713 }
2714 }
2715 // the casese true and null for a match are through, the
2716 // remaining case is false and that means adding the method
2717 // to our list
2718 list.add(method);
2719 }
2720
2721 private int findMatchingMethod(CachedMethod[] data, int from, int to, MetaMethod method) {
2722 for (int j = from; j <= to; ++j) {
2723 CachedMethod aMethod = data[j];
2724 CachedClass[] params1 = aMethod.getParameterTypes();
2725 CachedClass[] params2 = method.getParameterTypes();
2726 if (params1.length == params2.length) {
2727 boolean matches = true;
2728 for (int i = 0; i < params1.length; i++) {
2729 if (params1[i] != params2[i]) {
2730 matches = false;
2731 break;
2732 }
2733 }
2734 if (matches) {
2735 return j;
2736 }
2737 }
2738 }
2739 return -1;
2740 }
2741
2742 /**
2743 * @return the matching method which should be found
2744 */
2745 private MetaMethod findMethod(CachedMethod aMethod) {
2746 Object methods = getMethods(theClass, aMethod.getName(), false);
2747 if (methods instanceof FastArray) {
2748 FastArray m = (FastArray) methods;
2749 final int len = m.size;
2750 final Object data[] = m.getArray();
2751 for (int i = 0; i != len; ++i) {
2752 MetaMethod method = (MetaMethod) data[i];
2753 if (method.isMethod(aMethod)) {
2754 return method;
2755 }
2756 }
2757 }
2758 else {
2759 MetaMethod method = (MetaMethod) methods;
2760 if (method.getName().equals(aMethod.getName())
2761 // TODO: shoulld be better check for case when only diff in modifiers can be SYNTETIC flag
2762 // && method.getModifiers() == aMethod.getModifiers()
2763 && method.getReturnType().equals(aMethod.getReturnType())
2764 && MetaMethod.equal(method.getParameterTypes(), aMethod.getParameterTypes())) {
2765 return method;
2766 }
2767 }
2768 //log.warning("Creating reflection based dispatcher for: " + aMethod);
2769 synchronized (aMethod.cachedClass) {
2770 return aMethod;
2771 }
2772 }
2773
2774
2775 /**
2776 * Chooses the correct method to use from a list of methods which match by
2777 * name.
2778 *
2779 * @param methodOrList the possible methods to choose from
2780 * @param arguments
2781 */
2782 protected Object chooseMethod(String methodName, Object methodOrList, Class[] arguments) {
2783 if (methodOrList instanceof MetaMethod) {
2784 if (((ParameterTypes) methodOrList).isValidMethod(arguments)) {
2785 return methodOrList;
2786 }
2787 return null;
2788 }
2789
2790 FastArray methods = (FastArray) methodOrList;
2791 if (methods==null) return null;
2792 int methodCount = methods.size();
2793 if (methodCount <= 0) {
2794 return null;
2795 } else if (methodCount == 1) {
2796 Object method = methods.get(0);
2797 if (((ParameterTypes) method).isValidMethod(arguments)) {
2798 return method;
2799 }
2800 return null;
2801 }
2802 Object answer;
2803 if (arguments == null || arguments.length == 0) {
2804 answer = MetaClassHelper.chooseEmptyMethodParams(methods);
2805 } else if (arguments.length == 1 && arguments[0] == null) {
2806 answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
2807 } else {
2808 Object matchingMethods = null;
2809
2810 final int len = methods.size;
2811 Object data[] = methods.getArray();
2812 for (int i = 0; i != len; ++i) {
2813 Object method = data[i];
2814
2815 // making this false helps find matches
2816 if (((ParameterTypes) method).isValidMethod(arguments)) {
2817 if (matchingMethods == null)
2818 matchingMethods = method;
2819 else
2820 if (matchingMethods instanceof ArrayList)
2821 ((ArrayList)matchingMethods).add(method);
2822 else {
2823 ArrayList arr = new ArrayList(4);
2824 arr.add(matchingMethods);
2825 arr.add(method);
2826 matchingMethods = arr;
2827 }
2828 }
2829 }
2830 if (matchingMethods == null) {
2831 return null;
2832 } else if (!(matchingMethods instanceof ArrayList)) {
2833 return matchingMethods;
2834 }
2835 return chooseMostSpecificParams(methodName, (List) matchingMethods, arguments);
2836
2837 }
2838 if (answer != null) {
2839 return answer;
2840 }
2841 throw new MethodSelectionException(methodName, methods, arguments);
2842 }
2843
2844 private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
2845
2846 long matchesDistance = -1;
2847 LinkedList matches = new LinkedList();
2848 for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
2849 Object method = iter.next();
2850 ParameterTypes paramTypes = (ParameterTypes) method;
2851 long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
2852 if (dist == 0) return method;
2853 if (matches.size() == 0) {
2854 matches.add(method);
2855 matchesDistance = dist;
2856 } else if (dist < matchesDistance) {
2857 matchesDistance = dist;
2858 matches.clear();
2859 matches.add(method);
2860 } else if (dist == matchesDistance) {
2861 matches.add(method);
2862 }
2863
2864 }
2865 if (matches.size() == 1) {
2866 return matches.getFirst();
2867 }
2868 if (matches.size() == 0) {
2869 return null;
2870 }
2871
2872 //more than one matching method found --> ambiguous!
2873 String msg = "Ambiguous method overloading for method ";
2874 msg += theClass.getName() + "#" + name;
2875 msg += ".\nCannot resolve which method to invoke for ";
2876 msg += InvokerHelper.toString(arguments);
2877 msg += " due to overlapping prototypes between:";
2878 for (Iterator iter = matches.iterator(); iter.hasNext();) {
2879 Class[] types = ((ParameterTypes) iter.next()).getNativeParameterTypes();
2880 msg += "\n\t" + InvokerHelper.toString(types);
2881 }
2882 throw new GroovyRuntimeException(msg);
2883 }
2884
2885 private boolean isGenericGetMethod(MetaMethod method) {
2886 if (method.getName().equals("get")) {
2887 CachedClass[] parameterTypes = method.getParameterTypes();
2888 return parameterTypes.length == 1 && parameterTypes[0].getTheClass() == String.class;
2889 }
2890 return false;
2891 }
2892
2893
2894 public synchronized void initialize() {
2895 if (!isInitialized()) {
2896 fillMethodIndex();
2897 addProperties();
2898 initialized = true;
2899 }
2900 }
2901
2902 private void addProperties() {
2903 BeanInfo info;
2904 final Class stopClass;
2905 // introspect
2906 try {
2907 if (isBeanDerivative(theClass)) {
2908 info = (BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
2909 public Object run() throws IntrospectionException {
2910 return Introspector.getBeanInfo(theClass, Introspector.IGNORE_ALL_BEANINFO);
2911 }
2912 });
2913 } else {
2914 info = (BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
2915 public Object run() throws IntrospectionException {
2916 return Introspector.getBeanInfo(theClass);
2917 }
2918 });
2919 }
2920 } catch (PrivilegedActionException pae) {
2921 throw new GroovyRuntimeException("exception during bean introspection", pae.getException());
2922 }
2923 PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
2924 // build up the metaproperties based on the public fields, property descriptors,
2925 // and the getters and setters
2926 setupProperties(descriptors);
2927
2928 EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
2929 for (EventSetDescriptor descriptor : eventDescriptors) {
2930 Method[] listenerMethods = descriptor.getListenerMethods();
2931 for (Method listenerMethod : listenerMethods) {
2932 final MetaMethod metaMethod = CachedMethod.find(descriptor.getAddListenerMethod());
2933 addToAllMethodsIfPublic(metaMethod);
2934 String name = listenerMethod.getName();
2935 if (listeners.containsKey(name)) {
2936 listeners.put(name, AMBIGUOUS_LISTENER_METHOD);
2937 } else {
2938 listeners.put(name, metaMethod);
2939 }
2940 }
2941 }
2942 }
2943
2944 private boolean isBeanDerivative(Class theClass) {
2945 Class next = theClass;
2946 while (next != null) {
2947 if (Arrays.asList(next.getInterfaces()).contains(BeanInfo.class)) return true;
2948 next = next.getSuperclass();
2949 }
2950 return false;
2951 }
2952
2953 private void addToAllMethodsIfPublic(MetaMethod metaMethod) {
2954 if (Modifier.isPublic(metaMethod.getModifiers()))
2955 allMethods.add(metaMethod);
2956 }
2957
2958 public List<MetaMethod> getMethods() {
2959 return allMethods;
2960 }
2961
2962 public List<MetaMethod> getMetaMethods() {
2963 return new ArrayList<MetaMethod>(newGroovyMethodsSet);
2964 }
2965
2966 protected void dropStaticMethodCache(String name) {
2967 metaMethodIndex.clearCaches(name);
2968 }
2969
2970 protected void dropMethodCache(String name) {
2971 metaMethodIndex.clearCaches(name);
2972 }
2973
2974 public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args) {
2975 if (!(this instanceof AdaptingMetaClass)) {
2976 Class [] params = MetaClassHelper.convertToTypeArray(args);
2977 MetaMethod metaMethod = getMethodWithCachingInternal(getTheClass(), site, params);
2978 if (metaMethod != null)
2979 return PojoMetaMethodSite.createPojoMetaMethodSite(site, this, metaMethod, params, receiver, args);
2980 }
2981 return new PojoMetaClassSite(site, this);
2982 }
2983
2984
2985 public CallSite createStaticSite(CallSite site, Object[] args) {
2986 if (!(this instanceof AdaptingMetaClass)) {
2987 Class [] params = MetaClassHelper.convertToTypeArray(args);
2988 MetaMethod metaMethod = retrieveStaticMethod(site.getName(), args);
2989 if (metaMethod != null)
2990 return StaticMetaMethodSite.createStaticMetaMethodSite(site, this, metaMethod, params, args);
2991 }
2992 return new StaticMetaClassSite(site, this);
2993 }
2994
2995 public CallSite createPogoCallSite(CallSite site, Object[] args) {
2996 if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
2997 Class [] params = MetaClassHelper.convertToTypeArray(args);
2998 MetaMethod metaMethod = getMethodWithCachingInternal(theClass, site, params);
2999 if (metaMethod != null)
3000 return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args);
3001 }
3002 return new PogoMetaClassSite(site, this);
3003 }
3004
3005 public CallSite createPogoCallCurrentSite(CallSite site, Class sender, Object[] args) {
3006 if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
3007 Class [] params = MetaClassHelper.convertToTypeArray(args);
3008 MetaMethod metaMethod = getMethodWithCachingInternal(sender, site, params);
3009 if (metaMethod != null)
3010 return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args);
3011 }
3012 return new PogoMetaClassSite(site, this);
3013 }
3014
3015 public CallSite createConstructorSite(CallSite site, Object[] args) {
3016 if (!(this instanceof AdaptingMetaClass)) {
3017 Class[] params = MetaClassHelper.convertToTypeArray(args);
3018 CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, params);
3019 if (constructor != null) {
3020 return ConstructorSite.createConstructorSite(site, this,constructor,params, args);
3021 }
3022 else {
3023 if (args.length == 1 && args[0] instanceof Map) {
3024 constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY);
3025 if (constructor != null) {
3026 return new ConstructorSite.NoParamSite(site, this,constructor,params);
3027 }
3028 }
3029 }
3030 }
3031 return new MetaClassConstructorSite(site, this);
3032 }
3033
3034 public ClassInfo getClassInfo() {
3035 return theCachedClass.classInfo;
3036 }
3037
3038 public int getVersion() {
3039 return theCachedClass.classInfo.getVersion();
3040 }
3041
3042 public void incVersion() {
3043 theCachedClass.classInfo.incVersion();
3044 }
3045
3046 public MetaMethod[] getAdditionalMetaMethods() {
3047 return additionalMetaMethods;
3048 }
3049
3050 protected MetaBeanProperty findPropertyInClassHierarchy(String propertyName, CachedClass theClass) {
3051 MetaBeanProperty property= null;
3052 if (theClass == null)
3053 return null;
3054
3055 final CachedClass superClass = theClass.getCachedSuperClass();
3056 if (superClass == null)
3057 return null;
3058
3059 MetaClass metaClass = this.registry.getMetaClass(superClass.getTheClass());
3060 if(metaClass instanceof MutableMetaClass) {
3061 property = getMetaPropertyFromMutableMetaClass(propertyName,metaClass);
3062 if(property == null) {
3063 if(superClass != ReflectionCache.OBJECT_CLASS) {
3064 property = findPropertyInClassHierarchy(propertyName, superClass);
3065 }
3066 if(property == null) {
3067 final Class[] interfaces = theClass.getTheClass().getInterfaces();
3068 property = searchInterfacesForMetaProperty(propertyName, interfaces);
3069 }
3070 }
3071 }
3072 return property;
3073
3074 }
3075
3076 private MetaBeanProperty searchInterfacesForMetaProperty(String propertyName, Class[] interfaces) {
3077 MetaBeanProperty property = null;
3078 for (Class anInterface : interfaces) {
3079 MetaClass metaClass = registry.getMetaClass(anInterface);
3080 if (metaClass instanceof MutableMetaClass) {
3081 property = getMetaPropertyFromMutableMetaClass(propertyName, metaClass);
3082 if (property != null) break;
3083 }
3084 Class[] superInterfaces = anInterface.getInterfaces();
3085 if (superInterfaces.length > 0) {
3086 property = searchInterfacesForMetaProperty(propertyName, superInterfaces);
3087 if (property != null) break;
3088 }
3089
3090 }
3091 return property;
3092 }
3093
3094 private MetaBeanProperty getMetaPropertyFromMutableMetaClass(String propertyName, MetaClass metaClass) {
3095 final boolean isModified = ((MutableMetaClass) metaClass).isModified();
3096 if (isModified) {
3097 final MetaProperty metaProperty = metaClass.getMetaProperty(propertyName);
3098 if(metaProperty instanceof MetaBeanProperty)
3099 return (MetaBeanProperty)metaProperty;
3100 }
3101 return null;
3102 }
3103
3104 protected MetaMethod findMixinMethod(String methodName, Class[] arguments) {
3105 return null;
3106 }
3107
3108 protected static MetaMethod findMethodInClassHierarchy(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass) {
3109
3110 if (metaClass instanceof MetaClassImpl) {
3111 boolean check = false;
3112 for (ClassInfo ci : ((MetaClassImpl)metaClass).theCachedClass.getHierarchy ()) {
3113 final MetaClass aClass = ci.getStrongMetaClass();
3114 if (aClass instanceof MutableMetaClass && ((MutableMetaClass)aClass).isModified()) {
3115 check = true;
3116 break;
3117 }
3118 }
3119
3120 if (!check)
3121 return null;
3122 }
3123
3124 MetaMethod method = null;
3125
3126 Class superClass;
3127 if (metaClass.getTheClass().isArray() && !metaClass.getTheClass().getComponentType().isPrimitive() && metaClass.getTheClass().getComponentType() != Object.class) {
3128 superClass = Object[].class;
3129 }
3130 else {
3131 superClass = metaClass.getTheClass().getSuperclass();
3132 }
3133
3134 if (superClass != null) {
3135 MetaClass superMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(superClass);
3136 method = findMethodInClassHierarchy(instanceKlazz, methodName, arguments, superMetaClass);
3137 }
3138 else {
3139 if (metaClass.getTheClass().isInterface()) {
3140 MetaClass superMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(Object.class);
3141 method = findMethodInClassHierarchy(instanceKlazz, methodName, arguments, superMetaClass);
3142 }
3143 }
3144
3145 method = findSubClassMethod(instanceKlazz, methodName, arguments, metaClass, method);
3146
3147 MetaMethod infMethod = searchInterfacesForMetaMethod(instanceKlazz, methodName, arguments, metaClass);
3148 if (infMethod != null) {
3149 if (method == null)
3150 method = infMethod;
3151 else
3152 method = mostSpecific(method, infMethod, instanceKlazz);
3153 }
3154
3155 method = findOwnMethod(instanceKlazz, methodName, arguments, metaClass, method);
3156
3157 return method;
3158 }
3159
3160 private static MetaMethod findSubClassMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass, MetaMethod method) {
3161 if (metaClass instanceof MetaClassImpl) {
3162 Object list = ((MetaClassImpl) metaClass).getSubclassMetaMethods(methodName);
3163 if (list != null) {
3164 if (list instanceof MetaMethod) {
3165 MetaMethod m = (MetaMethod) list;
3166 if (m.getDeclaringClass().getTheClass().isAssignableFrom(instanceKlazz)) {
3167 if (m.isValidExactMethod(arguments)) {
3168 if (method == null)
3169 method = m;
3170 else {
3171 method = mostSpecific (method, m, instanceKlazz);
3172 }
3173 }
3174 }
3175 }
3176 else {
3177 FastArray arr = (FastArray) list;
3178 for (int i = 0; i != arr.size(); ++i) {
3179 MetaMethod m = (MetaMethod) arr.get(i);
3180 if (m.getDeclaringClass().getTheClass().isAssignableFrom(instanceKlazz)) {
3181 if (m.isValidExactMethod(arguments)) {
3182 if (method == null)
3183 method = m;
3184 else {
3185 method = mostSpecific (method, m, instanceKlazz);
3186 }
3187 }
3188 }
3189 }
3190 }
3191 }
3192 }
3193 return method;
3194 }
3195
3196 private static MetaMethod mostSpecific(MetaMethod method, MetaMethod newMethod, Class instanceKlazz) {
3197 Class newMethodC = newMethod.getDeclaringClass().getTheClass();
3198 Class methodC = method.getDeclaringClass().getTheClass();
3199
3200 if (!newMethodC.isAssignableFrom(instanceKlazz))
3201 return method;
3202
3203 if (newMethodC == methodC)
3204 return newMethod;
3205
3206 if (newMethodC.isAssignableFrom(methodC)) {
3207 return method;
3208 }
3209
3210 if (methodC.isAssignableFrom(newMethodC)) {
3211 return newMethod;
3212 }
3213
3214 return newMethod;
3215 }
3216
3217 private static MetaMethod searchInterfacesForMetaMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass) {
3218 Class[] interfaces = metaClass.getTheClass().getInterfaces();
3219
3220 MetaMethod method = null;
3221 for (Class anInterface : interfaces) {
3222 MetaClass infMetaClass = GroovySystem.getMetaClassRegistry().getMetaClass(anInterface);
3223 MetaMethod infMethod = searchInterfacesForMetaMethod(instanceKlazz, methodName, arguments, infMetaClass);
3224 if (infMethod != null) {
3225 if (method == null)
3226 method = infMethod;
3227 else
3228 method = mostSpecific(method, infMethod, instanceKlazz);
3229 }
3230 }
3231
3232 method = findSubClassMethod(instanceKlazz, methodName, arguments, metaClass, method);
3233
3234 method = findOwnMethod(instanceKlazz, methodName, arguments, metaClass, method);
3235
3236 return method;
3237 }
3238
3239 protected static MetaMethod findOwnMethod(Class instanceKlazz, String methodName, Class[] arguments, MetaClass metaClass, MetaMethod method) {
3240 // we trick ourselves here
3241 if (instanceKlazz == metaClass.getTheClass())
3242 return method;
3243
3244 MetaMethod ownMethod = metaClass.pickMethod(methodName, arguments);
3245 if (ownMethod != null) {
3246 if (method == null)
3247 method = ownMethod;
3248 else
3249 method = mostSpecific(method, ownMethod, instanceKlazz);
3250 }
3251 return method;
3252 }
3253
3254 protected Object getSubclassMetaMethods(String methodName) {
3255 return null;
3256 }
3257
3258 private abstract class MethodIndexAction {
3259 public void iterate() {
3260 final ComplexKeyHashMap.Entry[] table = metaMethodIndex.methodHeaders.getTable();
3261 int len = table.length;
3262 for (int i = 0; i != len; ++i) {
3263 for (SingleKeyHashMap.Entry classEntry = (SingleKeyHashMap.Entry) table[i];
3264 classEntry != null;
3265 classEntry = (SingleKeyHashMap.Entry) classEntry.next) {
3266
3267 Class clazz = (Class) classEntry.getKey();
3268
3269 if (skipClass(clazz)) continue;
3270
3271 MetaMethodIndex.Header header = (MetaMethodIndex.Header) classEntry.getValue();
3272 for (MetaMethodIndex.Entry nameEntry = header.head; nameEntry != null; nameEntry = nameEntry.nextClassEntry) {
3273 methodNameAction(clazz, nameEntry);
3274 }
3275 }
3276 }
3277 }
3278
3279 public abstract void methodNameAction(Class clazz, MetaMethodIndex.Entry methods);
3280
3281 public boolean skipClass(Class clazz) {
3282 return false;
3283 }
3284 }
3285
3286 public Object getProperty(Object object, String property) {
3287 return getProperty(theClass, object, property, false, false);
3288 }
3289
3290 public void setProperty(Object object, String property, Object newValue) {
3291 setProperty(theClass, object, property, newValue, false, false);
3292 }
3293
3294 public Object getAttribute(Object object, String attribute) {
3295 return getAttribute(theClass, object, attribute, false, false);
3296 }
3297
3298 public void setAttribute(Object object, String attribute, Object newValue) {
3299 setAttribute(theClass, object, attribute, newValue, false, false);
3300 }
3301
3302 public MetaMethod pickMethod(String methodName, Class[] arguments) {
3303 return getMethodWithoutCaching(theClass, methodName, arguments, false);
3304 }
3305
3306 /**
3307 * @deprecated use pickMethod instead
3308 */
3309 protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
3310 return pickMethod(methodName, arguments);
3311 }
3312
3313 /**
3314 * remove all method call cache entries. This should be done if a
3315 * method is added during runtime, but not by using a category.
3316 */
3317 protected void clearInvocationCaches() {
3318 metaMethodIndex.clearCaches ();
3319 }
3320
3321 private static final SingleKeyHashMap.Copier NAME_INDEX_COPIER = new SingleKeyHashMap.Copier() {
3322 public Object copy(Object value) {
3323 if (value instanceof FastArray)
3324 return ((FastArray) value).copy();
3325 else
3326 return value;
3327 }
3328 };
3329
3330 private static final SingleKeyHashMap.Copier METHOD_INDEX_COPIER = new SingleKeyHashMap.Copier() {
3331 public Object copy(Object value) {
3332 return SingleKeyHashMap.copy(new SingleKeyHashMap(false), (SingleKeyHashMap) value, NAME_INDEX_COPIER);
3333 }
3334 };
3335
3336 class MethodIndex extends Index {
3337 public MethodIndex(boolean b) {
3338 super(false);
3339 }
3340
3341 public MethodIndex(int size) {
3342 super(size);
3343 }
3344
3345 public MethodIndex() {
3346 super();
3347 }
3348
3349 MethodIndex copy() {
3350 return (MethodIndex) SingleKeyHashMap.copy(new MethodIndex(false), this, METHOD_INDEX_COPIER);
3351 }
3352
3353 protected Object clone() throws CloneNotSupportedException {
3354 return super.clone();
3355 }
3356 }
3357
3358 public static class Index extends SingleKeyHashMap {
3359
3360 public Index(int size) {
3361 }
3362
3363 public Index() {
3364 }
3365
3366 public Index(boolean size) {
3367 super(false);
3368 }
3369
3370 public SingleKeyHashMap getNotNull(CachedClass key) {
3371 Entry res = getOrPut(key);
3372 if (res.value == null) {
3373 res.value = new SingleKeyHashMap();
3374 }
3375 return (SingleKeyHashMap) res.value;
3376 }
3377
3378 public void put(CachedClass key, SingleKeyHashMap value) {
3379 ((Entry) getOrPut(key)).value = value;
3380 }
3381
3382 public SingleKeyHashMap getNullable(CachedClass clazz) {
3383 return (SingleKeyHashMap) get(clazz);
3384 }
3385
3386 public boolean checkEquals(ComplexKeyHashMap.Entry e, Object key) {
3387 return ((Entry) e).key.equals(key);
3388 }
3389 }
3390
3391 private static class DummyMetaMethod extends MetaMethod {
3392
3393 public int getModifiers() {
3394 return 0;
3395 }
3396
3397 public String getName() {
3398 return null;
3399 }
3400
3401 public Class getReturnType() {
3402 return null;
3403 }
3404
3405 public CachedClass getDeclaringClass() {
3406 return null;
3407 }
3408
3409 public ParameterTypes getParamTypes() {
3410 return null;
3411 }
3412
3413 public Object invoke(Object object, Object[] arguments) {
3414 return null;
3415 }
3416 }
3417
3418 private static class GetMethodMetaProperty extends MetaProperty {
3419 private final MetaMethod theMethod;
3420
3421 public GetMethodMetaProperty(String name, MetaMethod theMethod) {
3422 super(name, Object.class);
3423 this.theMethod = theMethod;
3424 }
3425
3426 public Object getProperty(Object object) {
3427 return theMethod.doMethodInvoke(object, new Object[]{name});
3428 }
3429
3430 public void setProperty(Object object, Object newValue) {
3431 throw new UnsupportedOperationException();
3432 }
3433 }
3434
3435 private static class GetBeanMethodMetaProperty extends MetaProperty {
3436 private final MetaMethod theMethod;
3437
3438 public GetBeanMethodMetaProperty(String name, MetaMethod theMethod) {
3439 super(name, Object.class);
3440 this.theMethod = theMethod;
3441 }
3442
3443 public Object getProperty(Object object) {
3444 return theMethod.doMethodInvoke(object, EMPTY_ARGUMENTS);
3445 }
3446
3447 public void setProperty(Object object, Object newValue) {
3448 throw new UnsupportedOperationException();
3449 }
3450 }
3451 }