1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.lang;
47
48 import java.beans.BeanInfo;
49 import java.beans.EventSetDescriptor;
50 import java.beans.IntrospectionException;
51 import java.beans.Introspector;
52 import java.beans.PropertyDescriptor;
53 import java.lang.reflect.Constructor;
54 import java.lang.reflect.Field;
55 import java.lang.reflect.Method;
56 import java.lang.reflect.Modifier;
57 import java.net.URL;
58 import java.security.AccessController;
59 import java.security.PrivilegedAction;
60 import java.security.PrivilegedActionException;
61 import java.security.PrivilegedExceptionAction;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.Comparator;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.Iterator;
70 import java.util.LinkedList;
71 import java.util.List;
72 import java.util.Map;
73 import java.util.Set;
74 import java.util.logging.Level;
75
76 import org.codehaus.groovy.GroovyBugError;
77 import org.codehaus.groovy.ast.ClassNode;
78 import org.codehaus.groovy.classgen.BytecodeHelper;
79 import org.codehaus.groovy.control.CompilationUnit;
80 import org.codehaus.groovy.control.Phases;
81 import org.codehaus.groovy.runtime.CurriedClosure;
82 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
83 import org.codehaus.groovy.runtime.DefaultMethodKey;
84 import org.codehaus.groovy.runtime.GroovyCategorySupport;
85 import org.codehaus.groovy.runtime.InvokerHelper;
86 import org.codehaus.groovy.runtime.MetaClassHelper;
87 import org.codehaus.groovy.runtime.MethodClosure;
88 import org.codehaus.groovy.runtime.MethodKey;
89 import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
90 import org.codehaus.groovy.runtime.NewStaticMetaMethod;
91 import org.codehaus.groovy.runtime.ReflectionMetaMethod;
92 import org.codehaus.groovy.runtime.Reflector;
93 import org.codehaus.groovy.runtime.TransformMetaMethod;
94 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
95 import org.codehaus.groovy.runtime.wrappers.Wrapper;
96 import org.objectweb.asm.ClassVisitor;
97
98 /***
99 * Allows methods to be dynamically added to existing classes at runtime
100 *
101 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
102 * @author Guillaume Laforge
103 * @author Jochen Theodorou
104 * @version $Revision: 4669 $
105 * @see groovy.lang.MetaClass
106 */
107 public class MetaClassImpl extends MetaClass {
108
109 protected MetaClassRegistry registry;
110 private ClassNode classNode;
111 private Map classMethodIndex = new HashMap();
112 private Map classMethodIndexForSuper;
113 private Map classStaticMethodIndex = new HashMap();
114 private Map classPropertyIndex = new HashMap();
115 private Map classPropertyIndexForSuper = new HashMap();
116 private Map staticPropertyIndex = new HashMap();
117 private Map listeners = new HashMap();
118 private Map methodCache = Collections.synchronizedMap(new HashMap());
119 private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
120 private MetaMethod genericGetMethod;
121 private MetaMethod genericSetMethod;
122 private List constructors;
123 private List allMethods = new ArrayList();
124 private List interfaceMethods;
125 private Reflector reflector;
126 private boolean initialized;
127
128 private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
129 private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new Class[]{},null,0);
130 private static final Object[] EMPTY_ARGUMENTS = {};
131 private List newGroovyMethodsList = new LinkedList();
132
133 public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
134 super(theClass);
135 this.registry = registry;
136
137 constructors = (List) AccessController.doPrivileged(new PrivilegedAction() {
138 public Object run() {
139 return Arrays.asList (theClass.getDeclaredConstructors());
140 }
141 });
142 }
143
144 private void fillMethodIndex() {
145 LinkedList superClasses = getSuperClasses();
146
147 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
148 Class c = (Class) iter.next();
149 addMethods(c);
150 }
151
152 Set interfaces = new HashSet();
153 makeInterfaceSet(theClass,interfaces);
154
155 inheritMethods(superClasses,classMethodIndex);
156 inheritInterfaceMethods(interfaces);
157 copyClassMethodIndexForSuper();
158
159 connectMultimethods(superClasses);
160 populateInterfaces(interfaces);
161 removeMultimethodsOverloadedWithPrivateMethods();
162
163 replaceWithMOPCalls();
164 }
165
166 private LinkedList getSuperClasses() {
167 LinkedList superClasses = new LinkedList();
168 for (Class c = theClass; c!= null; c = c.getSuperclass()) {
169 superClasses.addFirst(c);
170 }
171 if (theClass.isArray() && theClass!=Object[].class && !theClass.getComponentType().isPrimitive()) {
172 superClasses.addFirst(Object[].class);
173 }
174 return superClasses;
175 }
176
177 private void removeMultimethodsOverloadedWithPrivateMethods() {
178 Map privates = new HashMap();
179 MethodIndexAction mia = new MethodIndexAction() {
180 public List methodNameAction(Class clazz, String methodName, List methods) {
181 boolean hasPrivate=false;
182 for (Iterator iter = methods.iterator(); iter.hasNext();) {
183 MetaMethod method = (MetaMethod) iter.next();
184 if (method.isPrivate() && clazz == method.getDeclaringClass()) {
185 hasPrivate = true;
186 break;
187 }
188 }
189 if (!hasPrivate) return null;
190
191
192
193
194
195
196 methods.clear();
197 methods.addAll((Collection) ((Map) classMethodIndexForSuper.get(clazz)).get(methodName));
198 return methods;
199 }
200 public boolean replaceMethodList() {return false;}
201 };
202 mia.iterate(classMethodIndex);
203 }
204
205
206 private void replaceWithMOPCalls() {
207
208 if (!GroovyObject.class.isAssignableFrom(theClass)) return;
209
210 final Map mainClassMethodIndex = (Map) classMethodIndex.get(theClass);
211 class MOPIter extends MethodIndexAction {
212 boolean useThis;
213 public boolean skipClass(Class clazz) {
214 return !useThis && clazz==theClass;
215 }
216 public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {
217 String mopName = getMOPMethodName(method.getDeclaringClass(), methodName,useThis);
218 List matches = (List) mainClassMethodIndex.get(mopName);
219 if (matches==null) {
220 newList.add(method);
221 return;
222 }
223 matches = new ArrayList(matches);
224 MetaMethod matchingMethod = removeMatchingMethod(matches,method);
225 if (matchingMethod==null) {
226 newList.add(method);
227 return;
228 } else {
229 newList.add(matchingMethod);
230 }
231 }
232 }
233 MOPIter iter = new MOPIter();
234
235
236 iter.useThis = false;
237 iter.iterate(classMethodIndexForSuper);
238
239 iter.useThis = true;
240 iter.iterate(classMethodIndex);
241 }
242
243 private String getMOPMethodName(Class declaringClass, String name, boolean useThis) {
244 int distance = 0;
245 for (;declaringClass!=null; declaringClass=declaringClass.getSuperclass()) {
246 distance++;
247 }
248 return (useThis?"this":"super")+"$"+distance+"$"+name;
249 }
250
251 private void copyClassMethodIndexForSuper() {
252 classMethodIndexForSuper = new HashMap(classMethodIndex.size());
253 for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) {
254 Map.Entry cmiEntry = (Map.Entry) iter.next();
255 Map methodIndex = (Map) cmiEntry.getValue();
256 Map copy = new HashMap (methodIndex.size());
257 for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) {
258 Map.Entry mEntry = (Map.Entry) iterator.next();
259 copy.put(mEntry.getKey(), new ArrayList((List) mEntry.getValue()));
260 }
261 classMethodIndexForSuper.put(cmiEntry.getKey(),copy);
262 }
263 }
264
265 private void inheritInterfaceMethods(Set interfaces) {
266
267 List methods = registry.getInstanceMethods();
268 for (Iterator iter = methods.iterator(); iter.hasNext();) {
269 Method element = (Method) iter.next();
270 Class dgmClass = element.getParameterTypes()[0];
271 if (!interfaces.contains(dgmClass)) continue;
272 NewInstanceMetaMethod method = new NewInstanceMetaMethod(createMetaMethod(element));
273 if (! newGroovyMethodsList.contains(method)){
274 newGroovyMethodsList.add(method);
275 }
276 Map methodIndex = (Map) classMethodIndex.get(theClass);
277 List list = (List) methodIndex.get(method.getName());
278 if (list == null) {
279 list = new ArrayList();
280 methodIndex.put(method.getName(), list);
281 list.add(method);
282 } else {
283 addMethodToList(list,method);
284 }
285 }
286 methods = registry.getStaticMethods();
287 for (Iterator iter = methods.iterator(); iter.hasNext();) {
288 Method element = (Method) iter.next();
289 Class dgmClass = element.getParameterTypes()[0];
290 if (!interfaces.contains(dgmClass)) continue;
291 addNewStaticMethod(element);
292 }
293 }
294
295 private void populateInterfaces(Set interfaces){
296 Map currentIndex = (Map) classMethodIndex.get(theClass);
297 Map index = new HashMap();
298 copyNonPrivateMethods(currentIndex,index);
299 for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
300 Class iClass = (Class) iter.next();
301 Map methodIndex = (Map) classMethodIndex.get(iClass);
302 if (methodIndex==null || methodIndex.size()==0) {
303 classMethodIndex.put(iClass,index);
304 continue;
305 }
306 copyNonPrivateMethods(currentIndex,methodIndex);
307 }
308 }
309
310 private static void makeInterfaceSet(Class c, Set s) {
311 if (c==null) return;
312 Class[] interfaces = c.getInterfaces();
313 for (int i = 0; i < interfaces.length; i++) {
314 if (!s.contains(interfaces[i])) {
315 s.add(interfaces[i]);
316 makeInterfaceSet(interfaces[i],s);
317 }
318 }
319 makeInterfaceSet(c.getSuperclass(),s);
320 }
321
322 private void copyNonPrivateMethods(Map from, Map to) {
323 for (Iterator iterator = from.entrySet().iterator(); iterator.hasNext();) {
324 Map.Entry element = (Map.Entry) iterator.next();
325 List oldList = (List) element.getValue();
326 List newList = (List) to.get(element.getKey());
327 if (newList==null) {
328 to.put(element.getKey(),new ArrayList(oldList));
329 } else {
330 addNonPrivateMethods(newList,oldList);
331 }
332 }
333 }
334
335 private void connectMultimethods(List superClasses){
336 superClasses = DefaultGroovyMethods.reverse(superClasses);
337 Map last = null;
338 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
339 Class c = (Class) iter.next();
340 Map methodIndex = (Map) classMethodIndex.get(c);
341 if (methodIndex==last) continue;
342 if (last!=null) copyNonPrivateMethods(last,methodIndex);
343 last = methodIndex;
344 }
345 }
346
347 private void inheritMethods(Collection superClasses, Map classMethodIndex){
348 Map last = null;
349 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
350 Class c = (Class) iter.next();
351 Map methodIndex = (Map) classMethodIndex.get(c);
352 if (last!=null) {
353 if (methodIndex.size()==0) {
354 classMethodIndex.put(c,last);
355 continue;
356 }
357 copyNonPrivateMethods(last,methodIndex);
358 }
359 last = methodIndex;
360 }
361 }
362
363 private void addNonPrivateMethods(List newList, List oldList) {
364 for (Iterator iter = oldList.iterator(); iter.hasNext();) {
365 MetaMethod element = (MetaMethod) iter.next();
366 if (element.isPrivate()) continue;
367 addMethodToList(newList,element);
368 }
369 }
370
371 /***
372 * @return all the normal instance methods avaiable on this class for the
373 * given name
374 */
375 private List getMethods(Class sender, String name, boolean isCallToSuper) {
376 Map methodIndex;
377 if (isCallToSuper) {
378 methodIndex = (Map) classMethodIndexForSuper.get(sender);
379 } else {
380 methodIndex = (Map) classMethodIndex.get(sender);
381 }
382 List answer;
383 if (methodIndex!=null) {
384 answer = (List) methodIndex.get(name);
385 if (answer == null) answer = Collections.EMPTY_LIST;
386 } else {
387 answer = Collections.EMPTY_LIST;
388 }
389
390 if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) {
391 List used = GroovyCategorySupport.getCategoryMethods(sender, name);
392 if (used != null) {
393 answer = new ArrayList(answer);
394 for (Iterator iter = used.iterator(); iter.hasNext();) {
395 MetaMethod element = (MetaMethod) iter.next();
396 removeMatchingMethod(answer,element);
397 }
398 answer.addAll(used);
399 }
400 }
401 return answer;
402 }
403
404 /***
405 * @return all the normal static methods avaiable on this class for the
406 * given name
407 */
408 private List getStaticMethods(Class sender, String name) {
409 Map methodIndex = (Map) classStaticMethodIndex.get(sender);
410 if (methodIndex == null) return Collections.EMPTY_LIST;
411 List answer = (List) methodIndex.get(name);
412 if (answer == null) return Collections.EMPTY_LIST;
413 return answer;
414 }
415
416 public void addNewInstanceMethod(Method method) {
417 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
418 if (! newGroovyMethodsList.contains(newMethod)){
419 newGroovyMethodsList.add(newMethod);
420 addMetaMethod(newMethod);
421 }
422 }
423
424 public void addNewStaticMethod(Method method) {
425 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
426 if (! newGroovyMethodsList.contains(newMethod)){
427 newGroovyMethodsList.add(newMethod);
428 addMetaMethod(newMethod);
429 }
430 }
431
432 private void unwrap(Object[] arguments) {
433
434
435
436
437 for (int i = 0; i != arguments.length; i++) {
438 if (arguments[i] instanceof Wrapper) {
439 arguments[i] = ((Wrapper)arguments[i]).unwrap();
440 }
441 }
442 }
443
444
445 /***
446 * Invokes the given method on the object.
447 * @deprecated
448 */
449 public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
450 return invokeMethod(theClass,object,methodName,originalArguments,false,false);
451 }
452
453
454 /***
455 * Invokes the given method on the object.
456 *
457 */
458 public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
459 checkInitalised();
460 if (object == null) {
461 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
462 }
463 if (log.isLoggable(Level.FINER)){
464 MetaClassHelper.logMethodCall(object, methodName, originalArguments);
465 }
466 Object[] arguments = originalArguments;
467 if (arguments==null) arguments = EMPTY_ARGUMENTS;
468 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
469 unwrap(arguments);
470
471 MetaMethod method = getMethodWithCaching(sender, methodName, argClasses, isCallToSuper);
472
473 if (method==null && arguments.length==1 && arguments[0] instanceof List) {
474 Object[] newArguments = ((List) arguments[0]).toArray();
475 Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
476 method = getMethodWithCaching(sender, methodName, newArgClasses, isCallToSuper);
477 if (method!=null) {
478 MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses, isCallToSuper);
479 method = new TransformMetaMethod(method) {
480 public Object invoke(Object object, Object[] arguments) {
481 Object firstArgument = arguments[0];
482 List list = (List) firstArgument;
483 arguments = list.toArray();
484 return super.invoke(object, arguments);
485 }
486 };
487 cacheInstanceMethod(methodKey, method);
488 return invokeMethod(sender,object,methodName, originalArguments, isCallToSuper, fromInsideClass);
489 }
490 }
491
492 boolean isClosure = object instanceof Closure;
493 if (isClosure) {
494 Closure closure = (Closure) object;
495 Object delegate = closure.getDelegate();
496 Object owner = closure.getOwner();
497
498
499 if ("call".equals(methodName) || "doCall".equals(methodName)) {
500 if (object.getClass()==MethodClosure.class) {
501 MethodClosure mc = (MethodClosure) object;
502 methodName = mc.getMethod();
503 Class ownerClass = owner.getClass();
504 if (owner instanceof Class) ownerClass = (Class) owner;
505 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
506 return ownerMetaClass.invokeMethod(ownerClass,owner,methodName,arguments,false,false);
507 } else if (object.getClass()==CurriedClosure.class) {
508 CurriedClosure cc = (CurriedClosure) object;
509
510 arguments = cc.getUncurriedArguments(arguments);
511 Class ownerClass = owner.getClass();
512 if (owner instanceof Class) ownerClass = (Class) owner;
513 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
514 return ownerMetaClass.invokeMethod(owner,methodName,arguments);
515 }
516 } else if ("curry".equals(methodName)) {
517 return closure.curry(arguments);
518 }
519
520 if (method==null && owner!=closure) {
521 Class ownerClass = owner.getClass();
522 if (owner instanceof Class) ownerClass = (Class) owner;
523 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
524 method = ownerMetaClass.pickMethod(methodName,argClasses);
525 if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments);
526 }
527 if (method==null && delegate!=closure && delegate!=null) {
528 Class delegateClass = delegate.getClass();
529 if (delegate instanceof Class) delegateClass = (Class) delegate;
530 MetaClass delegateMetaClass = registry.getMetaClass(delegateClass);
531 method = delegateMetaClass.pickMethod(methodName,argClasses);
532 if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments);
533 }
534 if (method==null) {
535
536
537 MissingMethodException last = null;
538 if (owner!=closure && (owner instanceof GroovyObject)) {
539 try {
540 GroovyObject go = (GroovyObject) owner;
541 return go.invokeMethod(methodName,originalArguments);
542 } catch (MissingMethodException mme) {
543 if (last==null) last = mme;
544 }
545 }
546 if (delegate!=closure && (delegate instanceof GroovyObject)) {
547 try {
548 GroovyObject go = (GroovyObject) delegate;
549 return go.invokeMethod(methodName,originalArguments);
550 } catch (MissingMethodException mme) {
551 last = mme;
552 }
553 }
554 if (last!=null) throw last;
555 }
556
557 }
558
559 if (method != null) {
560 return MetaClassHelper.doMethodInvoke(object, method, arguments);
561 } else {
562
563 try {
564 Object value = this.getProperty(object, methodName);
565 if (value instanceof Closure) {
566 Closure closure = (Closure) value;
567 MetaClass delegateMetaClass = closure.getMetaClass();
568 return delegateMetaClass.invokeMethod(closure.getClass(),closure,"doCall",originalArguments,false,fromInsideClass);
569 }
570 } catch (MissingPropertyException mpe) {}
571
572 throw new MissingMethodException(methodName, theClass, originalArguments, false);
573 }
574 }
575
576 public MetaMethod getMethodWithCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
577
578 if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) {
579 return getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
580 } else {
581 MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments, isCallToSuper);
582 MetaMethod method = (MetaMethod) methodCache.get(methodKey);
583 if (method == null) {
584 method = getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
585 cacheInstanceMethod(methodKey, method);
586 }
587 return method;
588 }
589 }
590
591 protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
592 if (method != null && method.isCacheable()) {
593 methodCache.put(key, method);
594 }
595 }
596
597 protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
598 if (method != null && method.isCacheable()) {
599 staticMethodCache.put(key, method);
600 }
601 }
602
603
604 public Constructor retrieveConstructor(Class[] arguments) {
605 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
606 if (constructor != null) {
607 return constructor;
608 }
609 constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
610 if (constructor != null) {
611 return constructor;
612 }
613 return null;
614 }
615
616 public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
617 MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments, false);
618 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
619 if (method == null) {
620 method = pickStaticMethod(theClass,methodName, arguments);
621 cacheStaticMethod(methodKey, method);
622 }
623 return method;
624 }
625
626 public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
627 MetaMethod method = null;
628 List methods = getMethods(sender,methodName,isCallToSuper);
629 if (methods!=null && !methods.isEmpty()) {
630 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
631 }
632 return method;
633 }
634
635 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
636 checkInitalised();
637 if (log.isLoggable(Level.FINER)){
638 MetaClassHelper.logMethodCall(object, methodName, arguments);
639 }
640
641 Class sender = object.getClass();
642 if (object instanceof Class) sender = (Class) object;
643 if (sender!=theClass) {
644 MetaClass mc = registry.getMetaClass(sender);
645 return mc.invokeStaticMethod(sender,methodName,arguments);
646 }
647 if (sender==Class.class) {
648 return invokeMethod(object,methodName,arguments);
649 }
650
651 if (arguments==null) arguments = EMPTY_ARGUMENTS;
652 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
653 unwrap(arguments);
654
655
656 MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses, false);
657 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
658 if (method == null) {
659 method = pickStaticMethod(sender, methodName, argClasses);
660 cacheStaticMethod(methodKey.createCopy(), method);
661 }
662
663 if (method != null) {
664 return MetaClassHelper.doMethodInvoke(object, method, arguments);
665 }
666
667 throw new MissingMethodException(methodName, sender, arguments, true);
668 }
669
670 private MetaMethod pickStaticMethod(Class sender, String methodName, Class[] arguments) {
671 MetaMethod method = null;
672 List methods = getStaticMethods(sender,methodName);
673
674 if (!methods.isEmpty()) {
675 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
676 }
677 if (method == null && theClass != Class.class) {
678 MetaClass classMetaClass = registry.getMetaClass(Class.class);
679 method = classMetaClass.pickMethod(methodName, arguments);
680 }
681 if (method == null) {
682 method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
683 }
684 return method;
685 }
686
687 public Object invokeConstructor(Object[] arguments) {
688 return invokeConstructor(theClass,arguments,false);
689 }
690
691 public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
692
693 if (numberOfCosntructors != constructors.size()) {
694 throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for "+
695 this.theClass.getName()+" do not match. Expected "+numberOfCosntructors+" but got "+constructors.size());
696 }
697
698 if (arguments==null) arguments = EMPTY_ARGUMENTS;
699 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
700 unwrap(arguments);
701 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
702 if (constructor == null) {
703 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
704 }
705 if (constructor==null) {
706 throw new GroovyRuntimeException(
707 "Could not find matching constructor for: "
708 + theClass.getName()
709 + "("+InvokerHelper.toTypeString(arguments)+")");
710 }
711 List l = new ArrayList(constructors);
712 Comparator comp = new Comparator() {
713 public int compare(Object arg0, Object arg1) {
714 Constructor c0 = (Constructor) arg0;
715 Constructor c1 = (Constructor) arg1;
716 String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getParameterTypes());
717 String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getParameterTypes());
718 return descriptor0.compareTo(descriptor1);
719 }
720 };
721 Collections.sort(l,comp);
722 int found = -1;
723 for (int i=0; i<l.size(); i++) {
724 if (l.get(i)!=constructor) continue;
725 found = i;
726 break;
727 }
728
729 int ret = 0 | (found << 8);
730 return ret;
731 }
732
733 /***
734 * checks if the initialisation of the class id complete.
735 * This method should be called as a form of assert, it is no
736 * way to test if there is still initialisation work to be done.
737 * Such logic must be implemented in a different way.
738 * @throws IllegalStateException if the initialisation is incomplete yet
739 */
740 protected void checkInitalised() {
741 if (!isInitialized())
742 throw new IllegalStateException(
743 "initialize must be called for meta " +
744 "class of "+ theClass +
745 "("+this.getClass() + ") " +
746 "to complete initialisation process " +
747 "before any invocation or field/property " +
748 "access can be done");
749 }
750
751 private Object invokeConstructor(Class at, Object[] arguments, boolean setAccessible) {
752 checkInitalised();
753 if (arguments==null) arguments = EMPTY_ARGUMENTS;
754 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
755 unwrap(arguments);
756 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
757 if (constructor != null) {
758 return doConstructorInvoke(at, constructor, arguments, true);
759 }
760 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
761 if (constructor != null) {
762 return doConstructorInvoke(at, constructor, arguments, true);
763 }
764
765 if (arguments.length == 1) {
766 Object firstArgument = arguments[0];
767 if (firstArgument instanceof Map) {
768 constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
769 if (constructor != null) {
770 Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true);
771 setProperties(bean, ((Map) firstArgument));
772 return bean;
773 }
774 }
775 }
776 throw new GroovyRuntimeException(
777 "Could not find matching constructor for: "
778 + theClass.getName()
779 + "("+InvokerHelper.toTypeString(arguments)+")");
780 }
781
782 /***
783 * Sets a number of bean properties from the given Map where the keys are
784 * the String names of properties and the values are the values of the
785 * properties to set
786 */
787 public void setProperties(Object bean, Map map) {
788 checkInitalised();
789 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
790 Map.Entry entry = (Map.Entry) iter.next();
791 String key = entry.getKey().toString();
792
793 Object value = entry.getValue();
794 setProperty(bean, key, value);
795 }
796 }
797
798 /***
799 * @return the given property's value on the object
800 */
801 public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
802 checkInitalised();
803
804
805
806
807 boolean isStatic = theClass != Class.class && object instanceof Class;
808 if (isStatic && object != theClass) {
809 MetaClass mc = registry.getMetaClass((Class) object);
810 return mc.getProperty(sender,object,name,useSuper,false);
811 }
812
813 MetaMethod method = null;
814 Object[] arguments = EMPTY_ARGUMENTS;
815
816
817
818
819 MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
820 if (mp != null) {
821 if (mp instanceof MetaBeanProperty) {
822 MetaBeanProperty mbp = (MetaBeanProperty) mp;
823 method = mbp.getGetter();
824 mp = mbp.getField();
825 }
826 }
827
828
829 if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
830 String getterName = "get"+MetaClassHelper.capitalize(name);
831 method = getCategoryMethodGetter(sender,getterName,false);
832 }
833
834
835
836
837 if (method==null && mp!=null) {
838 return mp.getProperty(object);
839 }
840
841
842
843
844
845
846 if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
847 method = getCategoryMethodGetter(sender,"get",true);
848 if (method!=null) arguments = new Object[]{name};
849 }
850
851
852
853 if (method==null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
854 arguments = new Object[]{ name };
855 method = genericGetMethod;
856 }
857
858
859
860
861 if (method==null) {
862 /*** todo these special cases should be special MetaClasses maybe */
863 if (theClass != Class.class && object instanceof Class) {
864 MetaClass mc = registry.getMetaClass(Class.class);
865 return mc.getProperty(Class.class,object,name,useSuper,false);
866 } else if (object instanceof Collection) {
867 return DefaultGroovyMethods.getAt((Collection) object, name);
868 } else if (object instanceof Object[]) {
869 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
870 } else {
871 MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
872 if (addListenerMethod != null) {
873
874 return null;
875 }
876 }
877 } else {
878
879
880
881
882 return MetaClassHelper.doMethodInvoke(object,method,arguments);
883 }
884
885
886
887
888 throw new MissingPropertyException(name, theClass);
889 }
890
891 private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
892 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
893 if (possibleGenericMethods != null) {
894 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
895 MetaMethod mmethod = (MetaMethod) iter.next();
896 Class[] paramTypes = mmethod.getParameterTypes();
897 if (useLongVersion) {
898 if (paramTypes.length==1 && paramTypes[0] == String.class) {
899 return mmethod;
900 }
901 } else {
902 if (paramTypes.length==0) return mmethod;
903 }
904 }
905 }
906 return null;
907 }
908
909 private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
910 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
911 if (possibleGenericMethods != null) {
912 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
913 MetaMethod mmethod = (MetaMethod) iter.next();
914 Class[] paramTypes = mmethod.getParameterTypes();
915 if (useLongVersion) {
916 if (paramTypes.length==2 && paramTypes[0] == String.class) {
917 return mmethod;
918 }
919 } else {
920 if (paramTypes.length==1) return mmethod;
921 }
922 }
923 }
924 return null;
925 }
926
927 /***
928 * Get all the properties defined for this type
929 * @return a list of MetaProperty objects
930 */
931 public List getProperties() {
932 checkInitalised();
933 Map propertyMap = (Map) classPropertyIndex.get(theClass);
934
935 List ret = new ArrayList(propertyMap.size());
936 for (Iterator iter = propertyMap.values().iterator(); iter.hasNext();) {
937 MetaProperty element = (MetaProperty) iter.next();
938 if (element instanceof MetaFieldProperty) continue;
939
940 if (element instanceof MetaBeanProperty) {
941 MetaBeanProperty mp = (MetaBeanProperty) element;
942 boolean setter = true;
943 boolean getter = true;
944 if (mp.getGetter()==null || mp.getGetter() instanceof NewInstanceMetaMethod) {
945 getter=false;
946 }
947 if (mp.getSetter()==null || mp.getSetter() instanceof NewInstanceMetaMethod) {
948 setter=false;
949 }
950 if (!setter && !getter) continue;
951 if (!setter && mp.getSetter()!=null) {
952 element = new MetaBeanProperty(mp.getName(),mp.getType(),mp.getGetter(),null);
953 }
954 if (!getter && mp.getGetter()!=null) {
955 element = new MetaBeanProperty(mp.getName(),mp.getType(), null, mp.getSetter());
956 }
957 }
958 ret.add(element);
959 }
960 return ret;
961 }
962
963 private MetaMethod findPropertyMethod(List methods, boolean isGetter) {
964 LinkedList ret = new LinkedList();
965 for (Iterator iter = methods.iterator(); iter.hasNext();) {
966 MetaMethod element = (MetaMethod) iter.next();
967 if ( !isGetter &&
968
969 element.getParameterTypes().length == 1)
970 {
971 ret.add(element);
972 }
973 if ( isGetter &&
974 !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
975 element.getParameterTypes().length == 0)
976 {
977 ret.add(element);
978 }
979 }
980 if (ret.size() == 0) return null;
981 if (ret.size() == 1) return (MetaMethod) ret.getFirst();
982
983
984
985
986
987
988 MetaMethod method = null;
989 int distance = -1;
990 for (Iterator iter = ret.iterator(); iter.hasNext();) {
991 MetaMethod element = (MetaMethod) iter.next();
992 Class c;
993 if (isGetter) {
994 c = element.getReturnType();
995 } else {
996 c = element.getParameterTypes()[0];
997 }
998 int localDistance = distanceToObject(c);
999
1000 if (distance==-1 || distance>localDistance) {
1001 distance = localDistance;
1002 method = element;
1003 }
1004 }
1005 return method;
1006 }
1007
1008 private static int distanceToObject(Class c) {
1009 int count;
1010 for (count=0; c!=null; count++) {
1011 c=c.getSuperclass();
1012 }
1013 return count;
1014 }
1015
1016
1017 /***
1018 * This will build up the property map (Map of MetaProperty objects, keyed on
1019 * property name).
1020 */
1021 private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
1022 LinkedList superClasses = getSuperClasses();
1023 Set interfaces = new HashSet();
1024 makeInterfaceSet(theClass,interfaces);
1025
1026
1027 if (theClass.isArray()) {
1028 Map map = new HashMap();
1029 map.put("length", arrayLengthProperty);
1030 classPropertyIndex.put(theClass,map);
1031 }
1032
1033 inheritStaticInterfaceFields(superClasses, interfaces);
1034 inheritFields(superClasses);
1035 applyPropertyDescriptors(propertyDescriptors);
1036
1037 applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex);
1038 applyStrayPropertyMethods(superClasses,classMethodIndexForSuper,classPropertyIndexForSuper);
1039
1040 copyClassPropertyIndexForSuper();
1041 makeStaticPropertyIndex();
1042 }
1043
1044 private void makeStaticPropertyIndex() {
1045 Map propertyMap = (Map) classPropertyIndex.get(theClass);
1046 for (Iterator iter = propertyMap.entrySet().iterator(); iter.hasNext();) {
1047 Map.Entry entry = (Map.Entry) iter.next();
1048 MetaProperty mp = (MetaProperty) entry.getValue();
1049 if (mp instanceof MetaFieldProperty) {
1050 MetaFieldProperty mfp = (MetaFieldProperty) mp;
1051 if (!mfp.isStatic()) continue;
1052 } else if (mp instanceof MetaBeanProperty) {
1053 MetaBeanProperty mbp = (MetaBeanProperty) mp;
1054 boolean getter = mbp.getGetter()==null || mbp.getGetter().isStatic();
1055 boolean setter = mbp.getSetter()==null || mbp.getSetter().isStatic();
1056 boolean field = mbp.getField()==null || mbp.getField().isStatic();
1057
1058 if (!getter && !setter && !field) {
1059 continue;
1060 } else if (setter && getter) {
1061 if (field) {
1062 mp = mbp;
1063 } else {
1064 mp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),mbp.getSetter());
1065 }
1066 } else if (getter && !setter) {
1067 if (mbp.getGetter()==null) {
1068 mp = mbp.getField();
1069 } else {
1070 MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),null);
1071 if (field) newmp.setField(mbp.getField());
1072 mp = newmp;
1073 }
1074 } else if (setter && !getter) {
1075 if (mbp.getSetter()==null) {
1076 mp = mbp.getField();
1077 } else {
1078 MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),null,mbp.getSetter());
1079 if (field) newmp.setField(mbp.getField());
1080 mp = newmp;
1081 }
1082 } else if (field) {
1083 mp = mbp.getField();
1084 }
1085 } else {
1086 continue;
1087 }
1088 if (mp==null) continue;
1089 staticPropertyIndex.put(entry.getKey(),mp);
1090 }
1091
1092 }
1093
1094 private void copyClassPropertyIndexForSuper() {
1095 for (Iterator iter = classPropertyIndex.entrySet().iterator(); iter.hasNext();) {
1096 Map.Entry entry = (Map.Entry) iter.next();
1097 HashMap newVal = new HashMap((Map)entry.getValue());
1098 classPropertyIndexForSuper.put(entry.getKey(),newVal);
1099 }
1100 }
1101
1102 private Map getMap2MapNotNull(Map m, Object key) {
1103 Map ret = (Map) m.get(key);
1104 if (ret==null) {
1105 ret = new HashMap();
1106 m.put(key,ret);
1107 }
1108 return ret;
1109 }
1110
1111 private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
1112 for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
1113 Class iclass = (Class) interfaceIter.next();
1114 Map iPropertyIndex = getMap2MapNotNull(classPropertyIndex,iclass);
1115 addFields(iclass,iPropertyIndex);
1116 for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
1117 Class sclass = (Class) classIter.next();
1118 if (! iclass.isAssignableFrom(sclass)) continue;
1119 Map sPropertyIndex = getMap2MapNotNull(classPropertyIndex,sclass);
1120 copyNonPrivateFields(iPropertyIndex,sPropertyIndex);
1121 }
1122 }
1123 }
1124
1125 private void inheritFields(LinkedList superClasses) {
1126 Map last = null;
1127 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
1128 Class klass = (Class) iter.next();
1129 Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
1130 if (last != null) {
1131 copyNonPrivateFields(last,propertyIndex);
1132 }
1133 last = propertyIndex;
1134 addFields(klass,propertyIndex);
1135 }
1136 }
1137
1138 private void addFields(final Class klass, Map propertyIndex) {
1139 Field[] fields = (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
1140 public Object run() {
1141 return klass.getDeclaredFields();
1142 }
1143 });
1144 for(int i = 0; i < fields.length; i++) {
1145 MetaFieldProperty mfp = new MetaFieldProperty(fields[i]);
1146 propertyIndex.put(fields[i].getName(), mfp);
1147 }
1148 }
1149
1150 private void copyNonPrivateFields(Map from, Map to) {
1151 for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) {
1152 Map.Entry entry = (Map.Entry) iter.next();
1153 MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue();
1154 if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
1155 to.put(entry.getKey(),mfp);
1156 }
1157 }
1158
1159 private void applyStrayPropertyMethods(LinkedList superClasses, Map classMethodIndex, Map classPropertyIndex) {
1160
1161 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
1162 Class klass = (Class) iter.next();
1163 Map methodIndex = (Map) classMethodIndex.get(klass);
1164 Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass);
1165 for (Iterator nameMethodIterator = methodIndex.entrySet().iterator(); nameMethodIterator.hasNext();) {
1166 Map.Entry entry = (Map.Entry) nameMethodIterator.next();
1167 String methodName = (String) entry.getKey();
1168
1169 if (methodName.length() < 4) continue;
1170
1171 boolean isGetter = methodName.startsWith("get");
1172 boolean isSetter = methodName.startsWith("set");
1173 if (!isGetter && !isSetter) continue;
1174
1175
1176 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
1177 MetaMethod propertyMethod = findPropertyMethod((List) entry.getValue(), isGetter);
1178 if (propertyMethod==null) continue;
1179
1180 createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
1181 }
1182 }
1183 }
1184
1185 private void createMetaBeanProperty(Map propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod){
1186
1187 MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
1188 if (mp == null) {
1189 if (isGetter) {
1190 mp = new MetaBeanProperty(propName,
1191 propertyMethod.getReturnType(),
1192 propertyMethod, null);
1193 } else {
1194
1195 mp = new MetaBeanProperty(propName,
1196 propertyMethod.getParameterTypes()[0],
1197 null, propertyMethod);
1198 }
1199 } else {
1200 MetaBeanProperty mbp;
1201 MetaFieldProperty mfp;
1202 if (mp instanceof MetaBeanProperty) {
1203 mbp = (MetaBeanProperty) mp;
1204 mfp = mbp.getField();
1205 } else if (mp instanceof MetaFieldProperty){
1206 mfp = (MetaFieldProperty) mp;
1207 mbp = new MetaBeanProperty(propName,
1208 mfp.getType(),
1209 null, null);
1210 } else {
1211 throw new GroovyBugError("unknown MetaProperty class used. Class is "+mp.getClass());
1212 }
1213
1214 if (isGetter && mbp.getGetter()==null) {
1215 mbp.setGetter(propertyMethod);
1216 } else if (!isGetter && mbp.getSetter()==null) {
1217 mbp.setSetter(propertyMethod);
1218 }
1219 mbp.setField(mfp);
1220 mp = mbp;
1221 }
1222 propertyIndex.put(propName, mp);
1223 }
1224
1225 private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
1226 Map propertyMap = (Map) classPropertyIndex.get(theClass);
1227
1228
1229 for(int i=0; i<propertyDescriptors.length; i++) {
1230 PropertyDescriptor pd = propertyDescriptors[i];
1231
1232
1233
1234
1235 if(pd.getPropertyType() == null)
1236 continue;
1237
1238
1239 Method method = pd.getReadMethod();
1240 MetaMethod getter;
1241 if(method != null)
1242 getter = findMethod(method);
1243 else
1244 getter = null;
1245
1246
1247 MetaMethod setter;
1248 method = pd.getWriteMethod();
1249 if(method != null)
1250 setter = findMethod(method);
1251 else
1252 setter = null;
1253
1254
1255 MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
1256
1257
1258 MetaFieldProperty field = null;
1259 MetaProperty old = (MetaProperty) propertyMap.get(pd.getName());
1260 if (old!=null) {
1261 if (old instanceof MetaBeanProperty) {
1262 field = ((MetaBeanProperty) old).getField();
1263 } else {
1264 field = (MetaFieldProperty) old;
1265 }
1266 mp.setField(field);
1267 }
1268
1269
1270
1271 propertyMap.put(pd.getName(), mp);
1272 }
1273 }
1274
1275 /***
1276 * Sets the property value on an object
1277 */
1278 public void setProperty(Class sender,Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
1279 checkInitalised();
1280
1281
1282
1283
1284 boolean isStatic = theClass != Class.class && object instanceof Class;
1285 if (isStatic && object != theClass) {
1286 MetaClass mc = registry.getMetaClass((Class) object);
1287 mc.getProperty(sender,object,name,useSuper,fromInsideClass);
1288 return;
1289 }
1290
1291
1292
1293
1294 if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap();
1295
1296
1297
1298 MetaMethod method = null;
1299 Object[] arguments = null;
1300
1301
1302
1303
1304 MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic);
1305 MetaProperty field = null;
1306 if (mp != null) {
1307 if (mp instanceof MetaBeanProperty) {
1308 MetaBeanProperty mbp = (MetaBeanProperty) mp;
1309 method = mbp.getSetter();
1310 if (method!=null) arguments = new Object[] { newValue };
1311 field = mbp.getField();
1312 } else {
1313 field = mp;
1314 }
1315 }
1316
1317
1318 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
1319 String getterName = "set"+MetaClassHelper.capitalize(name);
1320 method = getCategoryMethodSetter(sender,getterName,false);
1321 if (method!=null) arguments = new Object[] { newValue };
1322 }
1323
1324
1325
1326
1327 boolean ambigousListener = false;
1328 boolean usesProxy = false;
1329 if (method==null) {
1330 method = (MetaMethod) listeners.get(name);
1331 ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
1332 if ( method != null &&
1333 !ambigousListener &&
1334 newValue instanceof Closure)
1335 {
1336
1337 Object proxy =
1338 MetaClassHelper.createListenerProxy(method.getParameterTypes()[0], name, (Closure) newValue);
1339 arguments = new Object[] { proxy };
1340 newValue = proxy;
1341 usesProxy = true;
1342 } else {
1343 method = null;
1344 }
1345 }
1346
1347
1348
1349
1350 if (method==null && field!=null) {
1351 field.setProperty(object,newValue);
1352 return;
1353 }
1354
1355
1356
1357
1358