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.Array;
54 import java.lang.reflect.Constructor;
55 import java.lang.reflect.Field;
56 import java.lang.reflect.InvocationTargetException;
57 import java.lang.reflect.Method;
58 import java.lang.reflect.Modifier;
59 import java.net.URL;
60 import java.security.AccessController;
61 import java.security.PrivilegedAction;
62 import java.security.PrivilegedActionException;
63 import java.security.PrivilegedExceptionAction;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.HashMap;
69 import java.util.Iterator;
70 import java.util.LinkedList;
71 import java.util.List;
72 import java.util.Map;
73 import java.util.logging.Level;
74
75 import org.codehaus.groovy.ast.ClassNode;
76 import org.codehaus.groovy.classgen.ReflectorGenerator;
77 import org.codehaus.groovy.control.CompilationUnit;
78 import org.codehaus.groovy.control.CompilerConfiguration;
79 import org.codehaus.groovy.control.Phases;
80 import org.codehaus.groovy.runtime.CurriedClosure;
81 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
82 import org.codehaus.groovy.runtime.GroovyCategorySupport;
83 import org.codehaus.groovy.runtime.InvokerHelper;
84 import org.codehaus.groovy.runtime.InvokerInvocationException;
85 import org.codehaus.groovy.runtime.MetaClassHelper;
86 import org.codehaus.groovy.runtime.MethodClosure;
87 import org.codehaus.groovy.runtime.MethodKey;
88 import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
89 import org.codehaus.groovy.runtime.NewStaticMetaMethod;
90 import org.codehaus.groovy.runtime.ReflectionMetaMethod;
91 import org.codehaus.groovy.runtime.Reflector;
92 import org.codehaus.groovy.runtime.TemporaryMethodKey;
93 import org.codehaus.groovy.runtime.TransformMetaMethod;
94 import org.objectweb.asm.ClassVisitor;
95 import org.objectweb.asm.ClassWriter;
96
97 /***
98 * Allows methods to be dynamically added to existing classes at runtime
99 *
100 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
101 * @author Guillaume Laforge
102 * @author Jochen Theodorou
103 * @version $Revision: 1.9 $
104 */
105 public class MetaClassImpl extends MetaClass {
106
107 protected MetaClassRegistry registry;
108 private ClassNode classNode;
109 private Map methodIndex = new HashMap();
110 private Map staticMethodIndex = new HashMap();
111
112 private Map propertyMap = Collections.synchronizedMap(new HashMap());
113 private Map listeners = new HashMap();
114 private Map methodCache = Collections.synchronizedMap(new HashMap());
115 private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
116 private MetaMethod genericGetMethod;
117 private MetaMethod genericSetMethod;
118 private List constructors;
119 private List allMethods = new ArrayList();
120 private List interfaceMethods;
121 private Reflector reflector;
122 private boolean initialised;
123
124 private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
125
126 public MetaClassImpl(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
127 super(theClass);
128 this.registry = registry;
129
130 constructors = (List) AccessController.doPrivileged(new PrivilegedAction() {
131 public Object run() {
132 return Arrays.asList (theClass.getDeclaredConstructors());
133 }
134 });
135
136 addMethods(theClass,true);
137
138
139 BeanInfo info = null;
140 try {
141 info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
142 public Object run() throws IntrospectionException {
143 return Introspector.getBeanInfo(theClass);
144 }
145 });
146 } catch (PrivilegedActionException pae) {
147 if (pae.getException() instanceof IntrospectionException) {
148 throw (IntrospectionException) pae.getException();
149 } else {
150 throw new RuntimeException(pae.getException());
151 }
152 }
153
154 PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
155
156
157
158 setupProperties(descriptors);
159
160
161
162
163
164
165
166
167 EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
168 for (int i = 0; i < eventDescriptors.length; i++) {
169 EventSetDescriptor descriptor = eventDescriptors[i];
170 Method[] listenerMethods = descriptor.getListenerMethods();
171 for (int j = 0; j < listenerMethods.length; j++) {
172 Method listenerMethod = listenerMethods[j];
173 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
174 listeners.put(listenerMethod.getName(), metaMethod);
175 }
176 }
177 }
178
179 private void addInheritedMethods() {
180 LinkedList superClasses = new LinkedList();
181 for (Class c = theClass.getSuperclass(); c!=Object.class && c!= null; c = c.getSuperclass()) {
182 superClasses.addFirst(c);
183 }
184
185 for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
186 Class c = (Class) iter.next();
187 addMethods(c,true);
188 addNewStaticMethodsFrom(c);
189 }
190
191
192 Class[] interfaces = theClass.getInterfaces();
193 for (int i = 0; i < interfaces.length; i++) {
194 addNewStaticMethodsFrom(interfaces[i]);
195 }
196
197
198
199 if (theClass != Object.class) {
200 addMethods(Object.class, false);
201 addNewStaticMethodsFrom(Object.class);
202 }
203
204 if (theClass.isArray() && !theClass.equals(Object[].class)) {
205 addNewStaticMethodsFrom(Object[].class);
206 }
207 }
208
209 /***
210 * @return all the normal instance methods avaiable on this class for the
211 * given name
212 */
213 private List getMethods(String name) {
214 List answer = (List) methodIndex.get(name);
215 List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
216 if (used != null) {
217 if (answer != null) {
218 used.addAll(answer);
219 }
220 answer = used;
221 }
222 if (answer == null) {
223 answer = Collections.EMPTY_LIST;
224 }
225 return answer;
226 }
227
228 /***
229 * @return all the normal static methods avaiable on this class for the
230 * given name
231 */
232 private List getStaticMethods(String name) {
233 List answer = (List) staticMethodIndex.get(name);
234 if (answer == null) {
235 return Collections.EMPTY_LIST;
236 }
237 return answer;
238 }
239
240 /***
241 * Allows static method definitions to be added to a meta class as if it
242 * was an instance method
243 *
244 * @param method
245 */
246 protected void addNewInstanceMethod(Method method) {
247 if (initialised) {
248 throw new RuntimeException("Already initialized, cannot add new method: " + method);
249 }
250 else {
251 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
252 if (! newGroovyMethodsList.contains(newMethod)){
253 newGroovyMethodsList.add(newMethod);
254 addMethod(newMethod,false);
255 }
256 }
257 }
258
259 protected void addNewStaticMethod(Method method) {
260 if (initialised) {
261 throw new RuntimeException("Already initialized, cannot add new method: " + method);
262 }
263 else {
264 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
265 if (! newGroovyMethodsList.contains(newMethod)){
266 newGroovyMethodsList.add(newMethod);
267 addMethod(newMethod,false);
268 }
269 }
270 }
271
272 /***
273 * Invokes the given method on the object.
274 *
275 */
276 public Object invokeMethod(Object object, String methodName, Object[] arguments) {
277 if (object == null) {
278 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
279 }
280 if (log.isLoggable(Level.FINER)){
281 MetaClassHelper.logMethodCall(object, methodName, arguments);
282 }
283
284 MetaMethod method = retrieveMethod(object, methodName, arguments);
285
286 boolean isClosure = object instanceof Closure;
287 if (isClosure) {
288 Closure closure = (Closure) object;
289 Object delegate = closure.getDelegate();
290 Object owner = closure.getOwner();
291
292 if ("call".equals(methodName) || "doCall".equals(methodName)) {
293 if (object.getClass()==MethodClosure.class) {
294 MethodClosure mc = (MethodClosure) object;
295 methodName = mc.getMethod();
296 MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
297 return ownerMetaClass.invokeMethod(owner,methodName,arguments);
298 } else if (object.getClass()==CurriedClosure.class) {
299 CurriedClosure cc = (CurriedClosure) object;
300
301 arguments = cc.getUncurriedArguments(arguments);
302 MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
303 return ownerMetaClass.invokeMethod(owner,methodName,arguments);
304 }
305 } else if ("curry".equals(methodName)) {
306 return closure.curry(arguments);
307 }
308
309 if (method==null && owner!=closure) {
310 MetaClass ownerMetaClass = registry.getMetaClass(owner.getClass());
311 method = ownerMetaClass.retrieveMethod(owner,methodName,arguments);
312 if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,arguments);
313 }
314 if (method==null && delegate!=closure && delegate!=null) {
315 MetaClass delegateMetaClass = registry.getMetaClass(delegate.getClass());
316 method = delegateMetaClass.retrieveMethod(delegate,methodName,arguments);
317 if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,arguments);
318 }
319 if (method==null) {
320
321
322 MissingMethodException last = null;
323 if (delegate!=closure && (delegate instanceof GroovyObject)) {
324 try {
325 GroovyObject go = (GroovyObject) delegate;
326 return go.invokeMethod(methodName,arguments);
327 } catch (MissingMethodException mme) {
328 last = mme;
329 }
330 }
331 if (owner!=closure && (owner instanceof GroovyObject)) {
332 try {
333 GroovyObject go = (GroovyObject) owner;
334 return go.invokeMethod(methodName,arguments);
335 } catch (MissingMethodException mme) {
336 if (last==null) last = mme;
337 }
338 }
339 if (last!=null) throw last;
340 }
341
342 }
343
344 if (method != null) {
345 return MetaClassHelper.doMethodInvoke(object, method, arguments);
346 } else {
347
348 try {
349 Object value = this.getProperty(object, methodName);
350 if (value instanceof Closure) {
351 Closure closure = (Closure) value;
352 MetaClass delegateMetaClass = registry.getMetaClass(closure.getClass());
353 return delegateMetaClass.invokeMethod(closure,"doCall",arguments);
354 }
355 } catch (MissingPropertyException mpe) {}
356
357 throw new MissingMethodException(methodName, theClass, arguments);
358 }
359 }
360
361 public MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
362
363 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
364 MetaMethod method = (MetaMethod) methodCache.get(methodKey);
365 if (method == null) {
366 method = pickMethod(owner, methodName, arguments);
367 if (method != null && method.isCacheable()) {
368 methodCache.put(methodKey.createCopy(), method);
369 }
370 }
371 return method;
372 }
373
374 public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
375
376 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
377 MetaMethod method = (MetaMethod) methodCache.get(methodKey);
378 if (method == null) {
379 method = pickMethod(methodName, arguments);
380 if (method != null && method.isCacheable()) {
381 methodCache.put(methodKey.createCopy(), method);
382 }
383 }
384 return method;
385 }
386
387 public Constructor retrieveConstructor(Class[] arguments) {
388 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
389 if (constructor != null) {
390 return constructor;
391 }
392 else {
393 constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
394 if (constructor != null) {
395 return constructor;
396 }
397 }
398 return null;
399 }
400
401 public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
402 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
403 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
404 if (method == null) {
405 method = pickStaticMethod(methodName, arguments);
406 if (method != null) {
407 staticMethodCache.put(methodKey.createCopy(), method);
408 }
409 }
410 return method;
411 }
412 /***
413 * Picks which method to invoke for the given object, method name and arguments
414 */
415 protected MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
416 MetaMethod method = null;
417 List methods = getMethods(methodName);
418 if (!methods.isEmpty()) {
419 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
420 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
421 if (method == null) {
422 int size = (arguments != null) ? arguments.length : 0;
423 if (size == 1) {
424 Object firstArgument = arguments[0];
425 if (firstArgument instanceof List) {
426
427
428
429
430 List list = (List) firstArgument;
431 arguments = list.toArray();
432 argClasses = MetaClassHelper.convertToTypeArray(arguments);
433 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
434 if (method==null) return null;
435 return new TransformMetaMethod(method) {
436 public Object invoke(Object object, Object[] arguments) throws Exception {
437 Object firstArgument = arguments[0];
438 List list = (List) firstArgument;
439 arguments = list.toArray();
440 return super.invoke(object, arguments);
441 }
442 };
443 }
444 }
445 }
446 }
447 return method;
448 }
449
450 /***
451 * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
452 * this method is used only by ClassGenerator for static binding
453 * @param methodName
454 * @param arguments
455 * @return
456 */
457 protected MetaMethod pickMethod(String methodName, Class[] arguments) {
458 MetaMethod method = null;
459 List methods = getMethods(methodName);
460 if (!methods.isEmpty()) {
461 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
462
463
464
465
466 }
467 return method;
468 }
469
470 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
471 if (log.isLoggable(Level.FINER)){
472 MetaClassHelper.logMethodCall(object, methodName, arguments);
473 }
474
475 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
476 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
477 if (method == null) {
478 method = pickStaticMethod(object, methodName, arguments);
479 if (method != null) {
480 staticMethodCache.put(methodKey.createCopy(), method);
481 }
482 }
483
484 if (method != null) {
485 return MetaClassHelper.doMethodInvoke(object, method, arguments);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506 throw new MissingMethodException(methodName, theClass, arguments);
507 }
508
509 private MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
510 MetaMethod method = null;
511 List methods = getStaticMethods(methodName);
512
513 if (!methods.isEmpty()) {
514 method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), false);
515 }
516
517 if (method == null && theClass != Class.class) {
518 MetaClass classMetaClass = registry.getMetaClass(Class.class);
519 method = classMetaClass.pickMethod(object, methodName, arguments);
520 }
521 if (method == null) {
522 method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
523 }
524 return method;
525 }
526
527 private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
528 MetaMethod method = null;
529 List methods = getStaticMethods(methodName);
530
531 if (!methods.isEmpty()) {
532 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
533
534
535
536
537 }
538
539 if (method == null && theClass != Class.class) {
540 MetaClass classMetaClass = registry.getMetaClass(Class.class);
541 method = classMetaClass.pickMethod(methodName, arguments);
542 }
543 return method;
544 }
545
546 public Object invokeConstructor(Object[] arguments) {
547 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
548 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
549 if (constructor != null) {
550 return MetaClassHelper.doConstructorInvoke(constructor, arguments);
551 }
552 else {
553 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
554 if (constructor != null) {
555 return MetaClassHelper.doConstructorInvoke(constructor, arguments);
556 }
557 }
558
559 if (arguments.length == 1) {
560 Object firstArgument = arguments[0];
561 if (firstArgument instanceof Map) {
562 constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
563 if (constructor != null) {
564 Object bean = MetaClassHelper.doConstructorInvoke(constructor, MetaClassHelper.EMPTY_ARRAY);
565 setProperties(bean, ((Map) firstArgument));
566 return bean;
567 }
568 }
569 }
570 throw new GroovyRuntimeException(
571 "Could not find matching constructor for: "
572 + theClass.getName()
573 + "("+InvokerHelper.toTypeString(arguments)+")");
574 }
575
576 public Object invokeConstructorAt(Class at, Object[] arguments) {
577 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
578 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
579 if (constructor != null) {
580 return doConstructorInvokeAt(at, constructor, arguments);
581 }
582 else {
583 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
584 if (constructor != null) {
585 return doConstructorInvokeAt(at, constructor, arguments);
586 }
587 }
588
589 if (arguments.length == 1) {
590 Object firstArgument = arguments[0];
591 if (firstArgument instanceof Map) {
592 constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
593 if (constructor != null) {
594 Object bean = doConstructorInvokeAt(at, constructor, MetaClassHelper.EMPTY_ARRAY);
595 setProperties(bean, ((Map) firstArgument));
596 return bean;
597 }
598 }
599 }
600 throw new GroovyRuntimeException(
601 "Could not find matching constructor for: "
602 + theClass.getName()
603 + "("+InvokerHelper.toTypeString(arguments)+")");
604 }
605
606 /***
607 * Sets a number of bean properties from the given Map where the keys are
608 * the String names of properties and the values are the values of the
609 * properties to set
610 */
611 public void setProperties(Object bean, Map map) {
612 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
613 Map.Entry entry = (Map.Entry) iter.next();
614 String key = entry.getKey().toString();
615
616
617 if(propertyMap.get(key) == null)
618 continue;
619
620 Object value = entry.getValue();
621 try {
622 setProperty(bean, key, value);
623 }
624 catch (GroovyRuntimeException e) {
625
626 /*** todo should replace this code with a getMetaProperty(key) != null check
627 i.e. don't try and set a non-existent property
628 */
629 }
630 }
631 }
632
633 /***
634 * @return the given property's value on the object
635 */
636 public Object getProperty(final Object object, final String property) {
637
638 MetaProperty mp = (MetaProperty) propertyMap.get(property);
639 if (mp != null) {
640 try {
641
642
643
644 return mp.getProperty(object);
645 }
646 catch(Exception e) {
647 throw new GroovyRuntimeException("Cannot read property: " + property);
648 }
649 }
650
651 if (genericGetMethod == null) {
652
653 List possibleGenericMethods = getMethods("get");
654 if (possibleGenericMethods != null) {
655 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
656 MetaMethod mmethod = (MetaMethod) i.next();
657 Class[] paramTypes = mmethod.getParameterTypes();
658 if (paramTypes.length == 1 && paramTypes[0] == String.class) {
659 Object[] arguments = {property};
660 Object answer = MetaClassHelper.doMethodInvoke(object, mmethod, arguments);
661 return answer;
662 }
663 }
664 }
665 }
666 else {
667 Object[] arguments = { property };
668 Object answer = MetaClassHelper.doMethodInvoke(object, genericGetMethod, arguments);
669
670 if (answer != null) {
671 return answer;
672 }
673 }
674
675 if (!CompilerConfiguration.isJsrGroovy()) {
676
677
678 List methods = getMethods(property);
679 if (!methods.isEmpty()) {
680 return new MethodClosure(object, property);
681 }
682 }
683
684
685
686 Exception lastException = null;
687 try {
688 if ( !(object instanceof Class) ) {
689 MetaMethod method = findGetter(object, "get" + MetaClassHelper.capitalize(property));
690 if (method != null) {
691 return MetaClassHelper.doMethodInvoke(object, method, MetaClassHelper.EMPTY_ARRAY);
692 }
693 }
694 }
695 catch (GroovyRuntimeException e) {
696 lastException = e;
697 }
698
699 /*** todo or are we an extensible groovy class? */
700 if (genericGetMethod != null) {
701 return null;
702 }
703 else {
704 /*** todo these special cases should be special MetaClasses maybe */
705 if (object instanceof Class) {
706
707 return getStaticProperty((Class) object, property);
708 }
709 if (object instanceof Collection) {
710 return DefaultGroovyMethods.getAt((Collection) object, property);
711 }
712 if (object instanceof Object[]) {
713 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
714 }
715 if (object instanceof Object) {
716 try {
717 return getAttribute(object,property);
718 } catch (MissingFieldException mfe) {
719
720 }
721 }
722
723 MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
724 if (addListenerMethod != null) {
725
726 return null;
727 }
728
729 if (lastException == null)
730 throw new MissingPropertyException(property, theClass);
731 else
732 throw new MissingPropertyException(property, theClass, lastException);
733 }
734 }
735
736 /***
737 * Get all the properties defined for this type
738 * @return a list of MetaProperty objects
739 */
740 public List getProperties() {
741
742 return new ArrayList(propertyMap.values());
743 }
744
745 /***
746 * This will build up the property map (Map of MetaProperty objects, keyed on
747 * property name).
748 */
749 private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
750 MetaProperty mp;
751 Method method;
752 MetaMethod getter = null;
753 MetaMethod setter = null;
754 Class klass;
755
756
757 klass = theClass;
758 while(klass != null) {
759 final Class clazz = klass;
760 Field[] fields = (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
761 public Object run() {
762 return clazz.getDeclaredFields();
763 }
764 });
765 for(int i = 0; i < fields.length; i++) {
766
767
768 if ((fields[i].getModifiers() & (java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.PROTECTED)) == 0)
769 continue;
770
771
772 if(propertyMap.get(fields[i].getName()) != null)
773 continue;
774
775
776
777
778 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
779 }
780
781
782 klass = klass.getSuperclass();
783 }
784
785
786 if (theClass.isArray()) {
787 propertyMap.put("length", arrayLengthProperty);
788 }
789
790
791
792 for(int i=0; i<propertyDescriptors.length; i++) {
793 PropertyDescriptor pd = propertyDescriptors[i];
794
795
796
797
798 if(pd.getPropertyType() == null)
799 continue;
800
801
802 method = pd.getReadMethod();
803 if(method != null)
804 getter = findMethod(method);
805 else
806 getter = null;
807
808
809 method = pd.getWriteMethod();
810 if(method != null)
811 setter = findMethod(method);
812 else
813 setter = null;
814
815
816
817
818
819 mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
820
821
822
823 propertyMap.put(pd.getName(), mp);
824 }
825
826
827 klass = theClass;
828 while(klass != null) {
829 final Class clazz = klass;
830 Method[] methods = (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
831 public Object run() {
832 return clazz.getDeclaredMethods();
833 }
834 });
835 for (int i = 0; i < methods.length; i++) {
836
837 if(Modifier.isPublic(methods[i].getModifiers()) == false)
838 continue;
839
840 method = methods[i];
841
842 String methodName = method.getName();
843
844
845 if(methodName.startsWith("get") &&
846 methodName.length() > 3 &&
847 method.getParameterTypes().length == 0) {
848
849
850 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
851
852
853 mp = (MetaProperty) propertyMap.get(propName);
854 if(mp != null) {
855
856 if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
857
858 ((MetaBeanProperty) mp).setGetter(findMethod(method));
859 }
860 }
861 else {
862
863
864 MetaBeanProperty mbp = new MetaBeanProperty(propName,
865 method.getReturnType(),
866 findMethod(method), null);
867
868
869 propertyMap.put(propName, mbp);
870 }
871 }
872 else if(methodName.startsWith("set") &&
873 methodName.length() > 3 &&
874 method.getParameterTypes().length == 1) {
875
876
877 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
878
879
880 mp = (MetaProperty) propertyMap.get(propName);
881 if(mp != null) {
882 if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
883
884 ((MetaBeanProperty) mp).setSetter(findMethod(method));
885 }
886 }
887 else {
888
889 MetaBeanProperty mbp = new MetaBeanProperty(propName,
890 method.getParameterTypes()[0],
891 null,
892 findMethod(method));
893
894
895 propertyMap.put(propName, mbp);
896 }
897 }
898 }
899
900
901 klass = klass.getSuperclass();
902 }
903 }
904
905 /***
906 * Sets the property value on an object
907 */
908 public void setProperty(Object object, String property, Object newValue) {
909 MetaProperty mp = (MetaProperty) propertyMap.get(property);
910 if(mp != null) {
911 try {
912 mp.setProperty(object, newValue);
913 return;
914 }
915 catch(ReadOnlyPropertyException e) {
916
917 throw e;
918 }
919 catch (TypeMismatchException e) {
920
921 throw e;
922 }
923 catch (Exception e) {
924
925
926 if (newValue == null)
927 return;
928 if (newValue instanceof List) {
929 List list = (List) newValue;
930 int params = list.size();
931 Constructor[] constructors = mp.getType().getConstructors();
932 for (int i = 0; i < constructors.length; i++) {
933 Constructor constructor = constructors[i];
934 if (constructor.getParameterTypes().length == params) {
935 Object value = MetaClassHelper.doConstructorInvoke(constructor, list.toArray());
936 mp.setProperty(object, value);
937 return;
938 }
939 }
940
941
942 Class parameterType = mp.getType();
943 if (parameterType.isArray()) {
944 Object objArray = MetaClassHelper.asPrimitiveArray(list, parameterType);
945 mp.setProperty(object, objArray);
946 return;
947 }
948 }
949
950
951
952
953
954 if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
955 MetaBeanProperty mbp = (MetaBeanProperty) mp;
956 List list = Arrays.asList((Object[])newValue);
957 MetaMethod setter = mbp.getSetter();
958
959 Class parameterType = setter.getParameterTypes()[0];
960 Class arrayType = parameterType.getComponentType();
961 Object objArray = Array.newInstance(arrayType, list.size());
962
963 for (int i = 0; i < list.size(); i++) {
964 List list2 =Arrays.asList((Object[]) list.get(i));
965 Object objArray2 = MetaClassHelper.asPrimitiveArray(list2, arrayType);
966 Array.set(objArray, i, objArray2);
967 }
968
969 MetaClassHelper.doMethodInvoke(object, setter, new Object[]{
970 objArray
971 });
972 return;
973 }
974
975 throw new MissingPropertyException(property, theClass, e);
976 }
977 }
978
979 try {
980 MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
981 if (addListenerMethod != null && newValue instanceof Closure) {
982
983 Object proxy =
984 MetaClassHelper.createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
985 MetaClassHelper.doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
986 return;
987 }
988
989 if (genericSetMethod == null) {
990
991 List possibleGenericMethods = getMethods("set");
992 if (possibleGenericMethods != null) {
993 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
994 MetaMethod mmethod = (MetaMethod) i.next();
995 Class[] paramTypes = mmethod.getParameterTypes();
996 if (paramTypes.length == 2 && paramTypes[0] == String.class) {
997 Object[] arguments = {property, newValue};
998 Object answer = MetaClassHelper.doMethodInvoke(object, mmethod, arguments);
999 return;
1000 }
1001 }
1002 }
1003 }
1004 else {
1005 Object[] arguments = { property, newValue };
1006 MetaClassHelper.doMethodInvoke(object, genericSetMethod, arguments);
1007 return;
1008 }
1009
1010 /*** todo or are we an extensible class? */
1011
1012
1013
1014
1015
1016
1017 String method = "set" + MetaClassHelper.capitalize(property);
1018 try {
1019 invokeMethod(object, method, new Object[] { newValue });
1020 }
1021 catch (MissingMethodException e1) {
1022 setAttribute(object,property,newValue);
1023 }
1024
1025 }
1026 catch (GroovyRuntimeException e) {
1027 throw new MissingPropertyException(property, theClass, e);
1028 }
1029
1030 }
1031
1032
1033 /***
1034 * Looks up the given attribute (field) on the given object
1035 */
1036 public Object getAttribute(final Object object, final String attribute) {
1037 PrivilegedActionException firstException = null;
1038
1039 final Class clazz;
1040 if (object instanceof Class) {
1041 clazz=(Class) object;
1042 } else {
1043 clazz=theClass;
1044 }
1045
1046 try {
1047 return AccessController.doPrivileged(new PrivilegedExceptionAction() {
1048 public Object run() throws NoSuchFieldException, IllegalAccessException {
1049 final Field field = clazz.getDeclaredField(attribute);
1050
1051 field.setAccessible(true);
1052 return field.get(object);
1053 }
1054 });
1055 } catch (final PrivilegedActionException pae) {
1056 firstException = pae;
1057 }
1058
1059 try {
1060 return AccessController.doPrivileged(new PrivilegedExceptionAction() {
1061 public Object run() throws NoSuchFieldException, IllegalAccessException {
1062 final Field field = clazz.getField(attribute);
1063
1064 field.setAccessible(true);
1065 return field.get(object);
1066 }
1067 });
1068 } catch (final PrivilegedActionException pae) {
1069
1070 }
1071
1072
1073 if (firstException.getException() instanceof NoSuchFieldException) {
1074 throw new MissingFieldException(attribute, theClass);
1075 } else {
1076 throw new RuntimeException(firstException.getException());
1077 }
1078 }
1079
1080 /***
1081 * Sets the given attribute (field) on the given object
1082 */
1083 public void setAttribute(final Object object, final String attribute, final Object newValue) {
1084 PrivilegedActionException firstException = null;
1085
1086 final Class clazz;
1087 if (object instanceof Class) {
1088 clazz=(Class) object;
1089 } else {
1090 clazz=theClass;
1091 }
1092
1093 try {
1094 AccessController.doPrivileged(new PrivilegedExceptionAction() {
1095 public Object run() throws NoSuchFieldException, IllegalAccessException {
1096 final Field field = clazz.getDeclaredField(attribute);
1097
1098 field.setAccessible(true);
1099 field.set(object,newValue);
1100 return null;
1101 }
1102 });
1103 return;
1104 } catch (final PrivilegedActionException pae) {
1105 firstException = pae;
1106 }
1107
1108 try {
1109 AccessController.doPrivileged(new PrivilegedExceptionAction() {
1110 public Object run() throws NoSuchFieldException, IllegalAccessException {
1111 final Field field = clazz.getField(attribute);
1112
1113 field.setAccessible(true);
1114 field.set(object, newValue);
1115 return null;
1116 }
1117 });
1118 return;
1119 } catch (final PrivilegedActionException pae) {
1120
1121 }
1122
1123 if (firstException.getException() instanceof NoSuchFieldException) {
1124 throw new MissingFieldException(attribute, theClass);
1125 } else {
1126 throw new RuntimeException(firstException.getException());
1127 }
1128 }
1129
1130 public ClassNode getClassNode() {
1131 if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
1132
1133 String className = theClass.getName();
1134 String groovyFile = className;
1135 int idx = groovyFile.indexOf('$');
1136 if (idx > 0) {
1137 groovyFile = groovyFile.substring(0, idx);
1138 }
1139 groovyFile = groovyFile.replace('.', '/') + ".groovy";
1140
1141
1142 URL url = theClass.getClassLoader().getResource(groovyFile);
1143 if (url == null) {
1144 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
1145 }
1146 if (url != null) {
1147 try {
1148
1149 /***
1150 * todo there is no CompileUnit in scope so class name
1151 * checking won't work but that mostly affects the bytecode
1152 * generation rather than viewing the AST
1153 */
1154 CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1155 public void call( ClassVisitor writer, ClassNode node ) {
1156 if( node.getName().equals(theClass.getName()) ) {
1157 MetaClassImpl.this.classNode = node;
1158 }
1159 }
1160 };
1161
1162
1163 CompilationUnit unit = new CompilationUnit(new GroovyClassLoader(getClass().getClassLoader()) );
1164 unit.setClassgenCallback( search );
1165 unit.addSource( url );
1166 unit.compile( Phases.CLASS_GENERATION );
1167 }
1168 catch (Exception e) {
1169 throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
1170 }
1171 }
1172
1173 }
1174 return classNode;
1175 }
1176
1177 public String toString() {
1178 return super.toString() + "[" + theClass + "]";
1179 }
1180
1181
1182
1183
1184 /***
1185 * Adds all the methods declared in the given class to the metaclass
1186 * ignoring any matching methods already defined by a derived class
1187 *
1188 * @param theClass
1189 */
1190 private void addMethods(final Class theClass, boolean forceOverwrite) {
1191
1192 Method[] methodArray = (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
1193 public Object run() {
1194 return theClass.getDeclaredMethods();
1195 }
1196 });
1197 for (int i = 0; i < methodArray.length; i++) {
1198 Method reflectionMethod = methodArray[i];
1199 if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
1200
1201 continue;
1202 }
1203 MetaMethod method = createMetaMethod(reflectionMethod);
1204 addMethod(method,forceOverwrite);
1205 }
1206 }
1207
1208 private void addMethod(MetaMethod method, boolean forceOverwrite) {
1209 String name = method.getName();
1210
1211
1212
1213 if (isGenericGetMethod(method) && genericGetMethod == null) {
1214 genericGetMethod = method;
1215 }
1216 else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
1217 genericSetMethod = method;
1218 }
1219 if (method.isStatic()) {
1220 List list = (List) staticMethodIndex.get(name);
1221 if (list == null) {
1222 list = new ArrayList();
1223 staticMethodIndex.put(name, list);
1224 list.add(method);
1225 }
1226 else {
1227 if (!MetaClassHelper.containsMatchingMethod(list, method)) {
1228 list.add(method);
1229 }
1230 }
1231 }
1232
1233 List list = (List) methodIndex.get(name);
1234 if (list == null) {
1235 list = new ArrayList();
1236 methodIndex.put(name, list);
1237 list.add(method);
1238 }
1239 else {
1240 if (forceOverwrite) {
1241 removeMatchingMethod(list,method);
1242 list.add(method);
1243 } else if (!MetaClassHelper.containsMatchingMethod(list, method)) {
1244 list.add(method);
1245 }
1246 }
1247 }
1248
1249 /***
1250 * remove a method of the same matching prototype was found in the list
1251 */
1252 private void removeMatchingMethod(List list, MetaMethod method) {
1253 for (Iterator iter = list.iterator(); iter.hasNext();) {
1254 MetaMethod aMethod = (MetaMethod) iter.next();
1255 Class[] params1 = aMethod.getParameterTypes();
1256 Class[] params2 = method.getParameterTypes();
1257 if (params1.length == params2.length) {
1258 boolean matches = true;
1259 for (int i = 0; i < params1.length; i++) {
1260 if (params1[i] != params2[i]) {
1261 matches = false;
1262 break;
1263 }
1264 }
1265 if (matches) {
1266 iter.remove();
1267 return;
1268 }
1269 }
1270 }
1271 return;
1272 }
1273
1274
1275 /***
1276 * Adds all of the newly defined methods from the given class to this
1277 * metaclass
1278 *
1279 * @param theClass
1280 */
1281 private void addNewStaticMethodsFrom(Class theClass) {
1282 MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
1283 Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
1284 while (iter.hasNext()) {
1285 MetaMethod method = (MetaMethod) iter.next();
1286 if (! newGroovyMethodsList.contains(method)){
1287 newGroovyMethodsList.add(method);
1288 addMethod(method,false);
1289 }
1290 }
1291 }
1292
1293 /***
1294 * @return the value of the static property of the given class
1295 */
1296 private Object getStaticProperty(Class aClass, String property) {
1297
1298
1299
1300
1301 MetaMethod method = findStaticGetter(aClass, "get" + MetaClassHelper.capitalize(property));
1302 if (method != null) {
1303 return MetaClassHelper.doMethodInvoke(aClass, method, MetaClassHelper.EMPTY_ARRAY);
1304 }
1305
1306
1307 try {
1308 return getAttribute(aClass,property);
1309 } catch (MissingFieldException mfe) {
1310 throw new MissingPropertyException(property, aClass, mfe);
1311 }
1312 }
1313
1314 /***
1315 * @return the matching method which should be found
1316 */
1317 private MetaMethod findMethod(Method aMethod) {
1318 List methods = getMethods(aMethod.getName());
1319 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1320 MetaMethod method = (MetaMethod) iter.next();
1321 if (method.isMethod(aMethod)) {
1322 return method;
1323 }
1324 }
1325
1326 return new ReflectionMetaMethod(aMethod);
1327 }
1328
1329 /***
1330 * @return the getter method for the given object
1331 */
1332 private MetaMethod findGetter(Object object, String name) {
1333 List methods = getMethods(name);
1334 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1335 MetaMethod method = (MetaMethod) iter.next();
1336 if (method.getParameterTypes().length == 0) {
1337 return method;
1338 }
1339 }
1340 return null;
1341 }
1342
1343 /***
1344 * @return the Method of the given name with no parameters or null
1345 */
1346 private MetaMethod findStaticGetter(Class type, String name) {
1347 List methods = getStaticMethods(name);
1348 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1349 MetaMethod method = (MetaMethod) iter.next();
1350 if (method.getParameterTypes().length == 0) {
1351 return method;
1352 }
1353 }
1354
1355 /*** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1356 try {
1357 Method method = type.getMethod(name, MetaClassHelper.EMPTY_TYPE_ARRAY);
1358 if ((method.getModifiers() & Modifier.STATIC) != 0) {
1359 return findMethod(method);
1360 }
1361 else {
1362 return null;
1363 }
1364 }
1365 catch (Exception e) {
1366 return null;
1367 }
1368 }
1369
1370 private static Object doConstructorInvokeAt(final Class at, Constructor constructor, Object[] argumentArray) {
1371 if (log.isLoggable(Level.FINER)) {
1372 MetaClassHelper.logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
1373 }
1374
1375 try {
1376
1377
1378 final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
1379
1380 final Constructor ctor = constructor;
1381 AccessController.doPrivileged(new PrivilegedAction() {
1382 public Object run() {
1383 ctor.setAccessible(accessible);
1384 return null;
1385 }
1386 });
1387
1388
1389 return constructor.newInstance(argumentArray);
1390 }
1391 catch (InvocationTargetException e) {
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401 throw new InvokerInvocationException(e);
1402 }
1403 catch (IllegalArgumentException e) {
1404 if (MetaClassHelper.coerceGStrings(argumentArray)) {
1405 try {
1406 return constructor.newInstance(argumentArray);
1407 }
1408 catch (Exception e2) {
1409
1410 }
1411 }
1412 throw new GroovyRuntimeException(
1413 "failed to invoke constructor: "
1414 + constructor
1415 + " with arguments: "
1416 + InvokerHelper.toString(argumentArray)
1417 + " reason: "
1418 + e);
1419 }
1420 catch (IllegalAccessException e) {
1421 throw new GroovyRuntimeException(
1422 "could not access constructor: "
1423 + constructor
1424 + " with arguments: "
1425 + InvokerHelper.toString(argumentArray)
1426 + " reason: "
1427 + e);
1428 }
1429 catch (Exception e) {
1430 throw new GroovyRuntimeException(
1431 "failed to invoke constructor: "
1432 + constructor
1433 + " with arguments: "
1434 + InvokerHelper.toString(argumentArray)
1435 + " reason: "
1436 + e,
1437 e);
1438 }
1439 }
1440
1441 /***
1442 * Chooses the correct method to use from a list of methods which match by
1443 * name.
1444 *
1445 * @param methods
1446 * the possible methods to choose from
1447 * @param arguments
1448 * the original argument to the method
1449 * @return
1450 */
1451 private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
1452 int methodCount = methods.size();
1453 if (methodCount <= 0) {
1454 return null;
1455 }
1456 else if (methodCount == 1) {
1457 Object method = methods.get(0);
1458 if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
1459 return method;
1460 }
1461 return null;
1462 }
1463 Object answer = null;
1464 if (arguments == null || arguments.length == 0) {
1465 answer = MetaClassHelper.chooseEmptyMethodParams(methods);
1466 }
1467 else if (arguments.length == 1 && arguments[0] == null) {
1468 answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
1469 }
1470 else {
1471 List matchingMethods = new ArrayList();
1472
1473 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1474 Object method = iter.next();
1475 Class[] paramTypes;
1476
1477
1478 if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
1479 matchingMethods.add(method);
1480 }
1481 }
1482 if (matchingMethods.isEmpty()) {
1483 return null;
1484 }
1485 else if (matchingMethods.size() == 1) {
1486 return matchingMethods.get(0);
1487 }
1488 return chooseMostSpecificParams(methodName, matchingMethods, arguments);
1489
1490 }
1491 if (answer != null) {
1492 return answer;
1493 }
1494 throw new GroovyRuntimeException(
1495 "Could not find which method to invoke from this list: "
1496 + methods
1497 + " for arguments: "
1498 + InvokerHelper.toString(arguments));
1499 }
1500
1501 private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
1502
1503 Class[] wrappedArguments = MetaClassHelper.wrap(arguments);
1504
1505 int matchesDistance = -1;
1506 LinkedList matches = new LinkedList();
1507 for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1508 Object method = iter.next();
1509 Class[] paramTypes = MetaClassHelper.getParameterTypes(method);
1510 if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
1511 int dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
1512 if (matches.size()==0) {
1513 matches.add(method);
1514 matchesDistance = dist;
1515 } else if (dist<matchesDistance) {
1516 matchesDistance=dist;
1517 matches.clear();
1518 matches.add(method);
1519 } else if (dist==matchesDistance) {
1520 matches.add(method);
1521 }
1522
1523 }
1524 if (matches.size()==1) {
1525 return matches.getFirst();
1526 }
1527 if (matches.size()==0) {
1528 return null;
1529 }
1530
1531
1532 String msg = "Ambiguous method overloading for method ";
1533 msg+= theClass.getName()+"#"+name;
1534 msg+= ".\nCannot resolve which method to invoke for ";
1535 msg+= InvokerHelper.toString(arguments);
1536 msg+= " due to overlapping prototypes between:";
1537 for (Iterator iter = matches.iterator(); iter.hasNext();) {
1538 Class[] types=MetaClassHelper.getParameterTypes(iter.next());
1539 msg+= "\n\t"+InvokerHelper.toString(types);
1540 }
1541 throw new GroovyRuntimeException(msg);
1542 }
1543
1544 private boolean isGenericGetMethod(MetaMethod method) {
1545 if (method.getName().equals("get")) {
1546 Class[] parameterTypes = method.getParameterTypes();
1547 return parameterTypes.length == 1 && parameterTypes[0] == String.class;
1548 }
1549 return false;
1550 }
1551
1552 /***
1553 * Call this method when any mutation method is called, such as adding a new
1554 * method to this MetaClass so that any caching or bytecode generation can be
1555 * regenerated.
1556 */
1557 private synchronized void onMethodChange() {
1558 reflector = null;
1559 }
1560
1561 protected synchronized void checkInitialised() {
1562 if (!initialised) {
1563 initialised = true;
1564 addInheritedMethods();
1565 }
1566 if (reflector == null) {
1567 generateReflector();
1568 }
1569 }
1570
1571 private MetaMethod createMetaMethod(final Method method) {
1572 if (registry.useAccessible()) {
1573 AccessController.doPrivileged(new PrivilegedAction() {
1574 public Object run() {
1575 method.setAccessible(true);
1576 return null;
1577 }
1578 });
1579 }
1580
1581 MetaMethod answer = new MetaMethod(method);
1582 if (isValidReflectorMethod(answer)) {
1583 allMethods.add(answer);
1584 answer.setMethodIndex(allMethods.size());
1585 }
1586 else {
1587
1588 answer = new ReflectionMetaMethod(method);
1589 }
1590
1591 if (useReflection) {
1592
1593 return new ReflectionMetaMethod(method);
1594 }
1595
1596 return answer;
1597 }
1598
1599 private boolean isValidReflectorMethod(MetaMethod method) {
1600
1601 if (!method.isPublic()) {
1602 return false;
1603 }
1604
1605 List interfaceMethods = getInterfaceMethods();
1606 for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) {
1607 MetaMethod aMethod = (MetaMethod) iter.next();
1608 if (method.isSame(aMethod)) {
1609 method.setInterfaceClass(aMethod.getDeclaringClass());
1610 return true;
1611 }
1612 }
1613
1614
1615 Class declaringClass = method.getDeclaringClass();
1616 for (Class clazz=declaringClass; clazz!=null; clazz=clazz.getSuperclass()) {
1617 try {
1618 final Class klazz = clazz;
1619 final String mName = method.getName();
1620 final Class[] parms = method.getParameterTypes();
1621 try {
1622 Method m = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
1623 public Object run() throws NoSuchMethodException {
1624 return klazz.getDeclaredMethod(mName, parms);
1625 }
1626 });
1627 if (!Modifier.isPublic(clazz.getModifiers())) continue;
1628 if (!Modifier.isPublic(m.getModifiers())) continue;
1629 declaringClass = clazz;
1630 } catch (PrivilegedActionException pae) {
1631 if (pae.getException() instanceof NoSuchMethodException) {
1632 throw (NoSuchMethodException) pae.getException();
1633 } else {
1634 throw new RuntimeException(pae.getException());
1635 }
1636 }
1637 } catch (SecurityException e) {
1638 continue;
1639 } catch (NoSuchMethodException e) {
1640 continue;
1641 }
1642 }
1643 if (!Modifier.isPublic(declaringClass.getModifiers())) return false;
1644 method.setDeclaringClass(declaringClass);
1645
1646 return true;
1647 }
1648
1649 private void generateReflector() {
1650 reflector = loadReflector(allMethods);
1651 if (reflector == null) {
1652 throw new RuntimeException("Should have a reflector for "+theClass.getName());
1653 }
1654
1655 for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
1656 MetaMethod metaMethod = (MetaMethod) iter.next();
1657
1658 metaMethod.setReflector(reflector);
1659 }
1660 }
1661
1662 private String getReflectorName() {
1663 String className = theClass.getName();
1664 String packagePrefix = "gjdk.";
1665 String name = packagePrefix + className + "_GroovyReflector";
1666 if (theClass.isArray()) {
1667 Class clazz = theClass;
1668 name = packagePrefix;
1669 int level = 0;
1670 while (clazz.isArray()) {
1671 clazz = clazz.getComponentType();
1672 level++;
1673 }
1674 String componentName = clazz.getName();
1675 name = packagePrefix + componentName + "_GroovyReflectorArray";
1676 if (level>1) name += level;
1677 }
1678 return name;
1679 }
1680
1681 private Reflector loadReflector(List methods) {
1682 ReflectorGenerator generator = new ReflectorGenerator(methods);
1683 String name = getReflectorName();
1684
1685
1686
1687 try {
1688 Class type = loadReflectorClass(name);
1689 return (Reflector) type.newInstance();
1690 }
1691 catch (ClassNotFoundException cnfe) {
1692
1693
1694
1695 try {
1696 ClassWriter cw = new ClassWriter(true);
1697 generator.generate(cw, name);
1698 byte[] bytecode = cw.toByteArray();
1699 Class type = loadReflectorClass(name, bytecode);
1700 if (Reflector.class.getClassLoader()!=type.getSuperclass().getClassLoader()) {
1701 throw new Error(
1702 name+" does have Reflector.class as superclass, "+
1703 "Reflector.class is loaded through the loader "+
1704 Reflector.class.getClassLoader()+
1705 " and "+name+"'s superclass is loaded through "+
1706 type.getSuperclass().getClassLoader()+
1707 ". This should never happen, check your classloader configuration."
1708 );
1709 }
1710 return (Reflector) type.newInstance();
1711 }
1712 catch (Exception e) {
1713 e.printStackTrace();
1714 throw new GroovyRuntimeException("Could not generate and load the reflector for class: " + name + ". Reason: " + e, e);
1715 }
1716 } catch (Error e) {
1717 throw e;
1718 } catch (Throwable t) {
1719
1720
1721
1722 throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + t, t);
1723 }
1724 }
1725
1726 private Class loadReflectorClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
1727 ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
1728 public Object run() {
1729 return theClass.getClassLoader();
1730 }
1731 });
1732 if (loader instanceof GroovyClassLoader) {
1733 final GroovyClassLoader gloader = (GroovyClassLoader) loader;
1734 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
1735 public Object run() {
1736 return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
1737 }
1738 });
1739 }
1740 return registry.loadClass(loader, name, bytecode);
1741 }
1742
1743 private Class loadReflectorClass(String name) throws ClassNotFoundException {
1744 ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
1745 public Object run() {
1746 return theClass.getClassLoader();
1747 }
1748 });
1749 if (loader instanceof GroovyClassLoader) {
1750 GroovyClassLoader gloader = (GroovyClassLoader) loader;
1751 return gloader.loadClass(name);
1752 }
1753 return registry.loadClass(loader, name);
1754 }
1755
1756 public List getMethods() {
1757 return allMethods;
1758 }
1759
1760 public List getMetaMethods() {
1761 return new ArrayList(newGroovyMethodsList);
1762 }
1763
1764 private synchronized List getInterfaceMethods() {
1765 if (interfaceMethods == null) {
1766 interfaceMethods = new ArrayList();
1767 Class type = theClass;
1768 while (type != null) {
1769 Class[] interfaces = type.getInterfaces();
1770 for (int i = 0; i < interfaces.length; i++) {
1771 Class iface = interfaces[i];
1772 Method[] methods = iface.getMethods();
1773 addInterfaceMethods(interfaceMethods, methods);
1774 }
1775 type = type.getSuperclass();
1776 }
1777 }
1778 return interfaceMethods;
1779 }
1780
1781 private void addInterfaceMethods(List list, Method[] methods) {
1782 for (int i = 0; i < methods.length; i++) {
1783 list.add(createMetaMethod(methods[i]));
1784 }
1785 }
1786 }