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