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.InvocationHandler;
57 import java.lang.reflect.InvocationTargetException;
58 import java.lang.reflect.Method;
59 import java.lang.reflect.Modifier;
60 import java.lang.reflect.Proxy;
61 import java.math.BigDecimal;
62 import java.math.BigInteger;
63 import java.net.URL;
64 import java.security.AccessControlException;
65 import java.security.AccessController;
66 import java.security.PrivilegedAction;
67 import java.security.PrivilegedActionException;
68 import java.security.PrivilegedExceptionAction;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Collection;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.logging.Logger;
78
79 import org.codehaus.groovy.ast.ClassNode;
80 import org.codehaus.groovy.classgen.ReflectorGenerator;
81 import org.codehaus.groovy.control.CompilationUnit;
82 import org.codehaus.groovy.control.Phases;
83 import org.codehaus.groovy.runtime.ClosureListener;
84 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
85 import org.codehaus.groovy.runtime.GroovyCategorySupport;
86 import org.codehaus.groovy.runtime.InvokerHelper;
87 import org.codehaus.groovy.runtime.InvokerInvocationException;
88 import org.codehaus.groovy.runtime.MethodClosure;
89 import org.codehaus.groovy.runtime.MethodHelper;
90 import org.codehaus.groovy.runtime.MethodKey;
91 import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
92 import org.codehaus.groovy.runtime.NewStaticMetaMethod;
93 import org.codehaus.groovy.runtime.ReflectionMetaMethod;
94 import org.codehaus.groovy.runtime.Reflector;
95 import org.codehaus.groovy.runtime.TemporaryMethodKey;
96 import org.codehaus.groovy.runtime.TransformMetaMethod;
97 import org.objectweb.asm.ClassVisitor;
98 import org.objectweb.asm.ClassWriter;
99
100 /***
101 * Allows methods to be dynamically added to existing classes at runtime
102 *
103 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
104 * @author Guillaume Laforge
105 * @version $Revision: 1.82 $
106 */
107 public class MetaClass {
108
109 private static final Logger log = Logger.getLogger(MetaClass.class.getName());
110
111 public static final Object[] EMPTY_ARRAY = {
112 };
113 public static Class[] EMPTY_TYPE_ARRAY = {
114 };
115 protected static final Object[] ARRAY_WITH_NULL = { null };
116
117 private static boolean useReflection = false;
118
119 private MetaClassRegistry registry;
120 private Class theClass;
121 private ClassNode classNode;
122 private Map methodIndex = new HashMap();
123 private Map staticMethodIndex = new HashMap();
124 private List newGroovyMethodsList = new ArrayList();
125
126 private Map propertyMap = Collections.synchronizedMap(new HashMap());
127 private Map listeners = new HashMap();
128 private Map methodCache = Collections.synchronizedMap(new HashMap());
129 private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
130 private MetaMethod genericGetMethod;
131 private MetaMethod genericSetMethod;
132 private List constructors;
133 private List allMethods = new ArrayList();
134 private List interfaceMethods;
135 private Reflector reflector;
136 private boolean initialised;
137
138 private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
139
140 public MetaClass(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
141 this.registry = registry;
142 this.theClass = theClass;
143
144 constructors = Arrays.asList(theClass.getDeclaredConstructors());
145 addMethods(theClass);
146
147
148 BeanInfo info = null;
149 try {
150 info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
151 public Object run() throws IntrospectionException {
152 return Introspector.getBeanInfo(theClass);
153 }
154 });
155 } catch (PrivilegedActionException pae) {
156 if (pae.getException() instanceof IntrospectionException) {
157 throw (IntrospectionException) pae.getException();
158 } else {
159 throw new RuntimeException(pae.getException());
160 }
161 }
162
163 PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
164
165
166
167 setupProperties(descriptors);
168
169
170
171
172
173
174
175
176 EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
177 for (int i = 0; i < eventDescriptors.length; i++) {
178 EventSetDescriptor descriptor = eventDescriptors[i];
179 Method[] listenerMethods = descriptor.getListenerMethods();
180 for (int j = 0; j < listenerMethods.length; j++) {
181 Method listenerMethod = listenerMethods[j];
182 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
183 listeners.put(listenerMethod.getName(), metaMethod);
184 }
185 }
186 }
187
188 public static boolean isUseReflection() {
189 return useReflection;
190 }
191
192 /***
193 * Allows reflection to be enabled in situations where bytecode generation
194 * of method invocations causes issues.
195 *
196 * @param useReflection
197 */
198 public static void setUseReflection(boolean useReflection) {
199 MetaClass.useReflection = useReflection;
200 }
201
202 private void addInheritedMethods(Class theClass) {
203
204 Class c = theClass;
205 if (c != Object.class) {
206 while (true) {
207 c = c.getSuperclass();
208 if (c == Object.class || c == null) {
209 break;
210 }
211 addMethods(c);
212 addNewStaticMethodsFrom(c);
213
214 }
215 }
216
217
218 Class[] interfaces = theClass.getInterfaces();
219 for (int i = 0; i < interfaces.length; i++) {
220 addNewStaticMethodsFrom(interfaces[i]);
221 }
222
223
224
225 if (theClass != Object.class) {
226 addMethods(Object.class);
227 addNewStaticMethodsFrom(Object.class);
228 }
229
230 if (theClass.isArray() && !theClass.equals(Object[].class)) {
231 addNewStaticMethodsFrom(Object[].class);
232 }
233 }
234
235 /***
236 * @return all the normal instance methods avaiable on this class for the
237 * given name
238 */
239 public List getMethods(String name) {
240 List answer = (List) methodIndex.get(name);
241 List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
242 if (used != null) {
243 if (answer != null) {
244 answer.addAll(used);
245 } else{
246 answer = used;
247 }
248 }
249 if (answer == null) {
250 answer = Collections.EMPTY_LIST;
251 }
252 return answer;
253 }
254
255 /***
256 * @return all the normal static methods avaiable on this class for the
257 * given name
258 */
259 public List getStaticMethods(String name) {
260 List answer = (List) staticMethodIndex.get(name);
261 if (answer == null) {
262 return Collections.EMPTY_LIST;
263 }
264 return answer;
265 }
266
267 /***
268 * Allows static method definitions to be added to a meta class as if it
269 * was an instance method
270 *
271 * @param method
272 */
273 protected void addNewInstanceMethod(Method method) {
274 if (initialised) {
275 throw new RuntimeException("Already initialized, cannot add new method: " + method);
276 }
277 else {
278 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
279 addMethod(newMethod);
280 addNewInstanceMethod(newMethod);
281 }
282 }
283
284 protected void addNewInstanceMethod(MetaMethod method) {
285 newGroovyMethodsList.add(method);
286 }
287
288 protected void addNewStaticMethod(Method method) {
289 if (initialised) {
290 throw new RuntimeException("Already initialized, cannot add new method: " + method);
291 }
292 else {
293 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
294 addMethod(newMethod);
295 addNewStaticMethod(newMethod);
296 }
297 }
298
299 protected void addNewStaticMethod(MetaMethod method) {
300 newGroovyMethodsList.add(method);
301 }
302
303 public Object invokeMethod(Object object, String methodName, Object arguments) {
304 return invokeMethod(object, methodName, asArray(arguments));
305 }
306
307 /***
308 * Invokes the given method on the object.
309 *
310 */
311 public Object invokeMethod(Object object, String methodName, Object[] arguments) {
312 if (object == null) {
313 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
314 }
315
316 MetaMethod method = retrieveMethod(object, methodName, arguments);
317
318 if (method != null) {
319 return doMethodInvoke(object, method, arguments);
320 } else {
321
322 try {
323 Object value = this.getProperty(object, methodName);
324 if (value instanceof Closure) {
325 Closure closure = (Closure) value;
326 closure.setDelegate(this);
327 return closure.call(arguments);
328 }
329 else {
330 throw new MissingMethodException(methodName, theClass, arguments);
331 }
332 }
333 catch (Exception e) {
334 throw new MissingMethodException(methodName, theClass, arguments);
335 }
336 }
337 }
338
339 protected MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
340
341 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
342 MetaMethod method = (MetaMethod) methodCache.get(methodKey);
343 if (method == null) {
344 method = pickMethod(owner, methodName, arguments);
345 if (method != null && method.isCacheable()) {
346 methodCache.put(methodKey.createCopy(), method);
347 }
348 }
349 return method;
350 }
351
352 public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
353
354 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
355 MetaMethod method = (MetaMethod) methodCache.get(methodKey);
356 if (method == null) {
357 method = pickMethod(methodName, arguments);
358 if (method != null && method.isCacheable()) {
359 methodCache.put(methodKey.createCopy(), method);
360 }
361 }
362 return method;
363 }
364
365 public Constructor retrieveConstructor(Class[] arguments) {
366 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
367 if (constructor != null) {
368 return constructor;
369 }
370 else {
371 constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
372 if (constructor != null) {
373 return constructor;
374 }
375 }
376 return null;
377 }
378
379 public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
380 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
381 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
382 if (method == null) {
383 method = pickStaticMethod(methodName, arguments);
384 if (method != null) {
385 staticMethodCache.put(methodKey.createCopy(), method);
386 }
387 }
388 return method;
389 }
390 /***
391 * Picks which method to invoke for the given object, method name and arguments
392 */
393 protected MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
394 MetaMethod method = null;
395 List methods = getMethods(methodName);
396 if (!methods.isEmpty()) {
397 Class[] argClasses = convertToTypeArray(arguments);
398 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, false);
399 if (method == null) {
400 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
401 if (method == null) {
402 int size = (arguments != null) ? arguments.length : 0;
403 if (size == 1) {
404 Object firstArgument = arguments[0];
405 if (firstArgument instanceof List) {
406
407
408
409
410 List list = (List) firstArgument;
411 arguments = list.toArray();
412 argClasses = convertToTypeArray(arguments);
413 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
414 return new TransformMetaMethod(method) {
415 public Object invoke(Object object, Object[] arguments) throws Exception {
416 Object firstArgument = arguments[0];
417 List list = (List) firstArgument;
418 arguments = list.toArray();
419 return super.invoke(object, arguments);
420 }
421 };
422 }
423 }
424 }
425 }
426 }
427 return method;
428 }
429
430 /***
431 * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
432 * this method is used only by ClassGenerator for static binding
433 * @param methodName
434 * @param arguments
435 * @return
436 */
437 protected MetaMethod pickMethod(String methodName, Class[] arguments) {
438 MetaMethod method = null;
439 List methods = getMethods(methodName);
440 if (!methods.isEmpty()) {
441 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
442
443
444
445
446 }
447 return method;
448 }
449
450 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
451
452
453
454
455
456
457 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
458 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
459 if (method == null) {
460 method = pickStaticMethod(object, methodName, arguments);
461 if (method != null) {
462 staticMethodCache.put(methodKey.createCopy(), method);
463 }
464 }
465
466 if (method != null) {
467 return doMethodInvoke(object, method, arguments);
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488 throw new MissingMethodException(methodName, theClass, arguments);
489 }
490
491 protected MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
492 MetaMethod method = null;
493 List methods = getStaticMethods(methodName);
494
495 if (!methods.isEmpty()) {
496 method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false);
497 }
498
499 if (method == null && theClass != Class.class) {
500 MetaClass classMetaClass = registry.getMetaClass(Class.class);
501 method = classMetaClass.pickMethod(object, methodName, arguments);
502 }
503 return method;
504 }
505
506 protected MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
507 MetaMethod method = null;
508 List methods = getStaticMethods(methodName);
509
510 if (!methods.isEmpty()) {
511 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
512
513
514
515
516 }
517
518 if (method == null && theClass != Class.class) {
519 MetaClass classMetaClass = registry.getMetaClass(Class.class);
520 method = classMetaClass.pickMethod(methodName, arguments);
521 }
522 return method;
523 }
524
525 public Object invokeConstructor(Object[] arguments) {
526 Class[] argClasses = convertToTypeArray(arguments);
527 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
528 if (constructor != null) {
529 return doConstructorInvoke(constructor, arguments);
530 }
531 else {
532 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
533 if (constructor != null) {
534 return doConstructorInvoke(constructor, arguments);
535 }
536 }
537
538 if (arguments.length == 1) {
539 Object firstArgument = arguments[0];
540 if (firstArgument instanceof Map) {
541 constructor = (Constructor) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false);
542 if (constructor != null) {
543 Object bean = doConstructorInvoke(constructor, EMPTY_ARRAY);
544 setProperties(bean, ((Map) firstArgument));
545 return bean;
546 }
547 }
548 }
549 throw new GroovyRuntimeException(
550 "Could not find matching constructor for: "
551 + theClass.getName()
552 + "("+InvokerHelper.toTypeString(arguments)+")");
553 }
554
555 /***
556 * Sets a number of bean properties from the given Map where the keys are
557 * the String names of properties and the values are the values of the
558 * properties to set
559 */
560 public void setProperties(Object bean, Map map) {
561 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
562 Map.Entry entry = (Map.Entry) iter.next();
563 String key = entry.getKey().toString();
564
565
566 if(propertyMap.get(key) == null)
567 continue;
568
569 Object value = entry.getValue();
570 try {
571 setProperty(bean, key, value);
572 }
573 catch (GroovyRuntimeException e) {
574
575 /*** todo should replace this code with a getMetaProperty(key) != null check
576 i.e. don't try and set a non-existent property
577 */
578 }
579 }
580 }
581
582 /***
583 * @return the given property's value on the object
584 */
585 public Object getProperty(final Object object, final String property) {
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 MetaProperty mp = (MetaProperty) propertyMap.get(property);
607 if(mp != null) {
608 try {
609
610
611
612 return mp.getProperty(object);
613 }
614 catch(Exception e) {
615 throw new GroovyRuntimeException("Cannot read property: " + property);
616 }
617 }
618
619 if (genericGetMethod == null) {
620
621 List possibleGenericMethods = getMethods("get");
622 if (possibleGenericMethods != null) {
623 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
624 MetaMethod mmethod = (MetaMethod) i.next();
625 Class[] paramTypes = mmethod.getParameterTypes();
626 if (paramTypes.length == 1 && paramTypes[0] == String.class) {
627 Object[] arguments = {property};
628 Object answer = doMethodInvoke(object, mmethod, arguments);
629 return answer;
630 }
631 }
632 }
633 }
634 else {
635 Object[] arguments = { property };
636 Object answer = doMethodInvoke(object, genericGetMethod, arguments);
637
638 if (answer != null) {
639 return answer;
640 }
641 }
642
643
644
645 List methods = getMethods(property);
646 if (!methods.isEmpty()) {
647 return new MethodClosure(object, property);
648 }
649
650
651
652 Exception lastException = null;
653 try {
654 MetaMethod method = findGetter(object, "get" + capitalize(property));
655 if (method != null) {
656 return doMethodInvoke(object, method, EMPTY_ARRAY);
657 }
658 }
659 catch (GroovyRuntimeException e) {
660 lastException = e;
661 }
662
663 /*** todo or are we an extensible groovy class? */
664 if (genericGetMethod != null) {
665 return null;
666 }
667 else {
668 /*** todo these special cases should be special MetaClasses maybe */
669 if (object instanceof Class) {
670
671 return getStaticProperty((Class) object, property);
672 }
673 if (object instanceof Collection) {
674 return DefaultGroovyMethods.getAt((Collection) object, property);
675 }
676 if (object instanceof Object[]) {
677 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
678 }
679 if (object instanceof Object) {
680 try {
681
682 Field field = object.getClass().getDeclaredField(property);
683 return field.get(object);
684 }
685 catch (Exception e1) {
686
687 }
688 }
689
690 MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
691 if (addListenerMethod != null) {
692
693 return null;
694 }
695
696 if (lastException == null)
697 throw new MissingPropertyException(property, theClass);
698 else
699 throw new MissingPropertyException(property, theClass, lastException);
700 }
701 }
702
703 /***
704 * Get all the properties defined for this type
705 * @return a list of MetaProperty objects
706 */
707 public List getProperties() {
708
709 return new ArrayList(propertyMap.values());
710 }
711
712 /***
713 * This will build up the property map (Map of MetaProperty objects, keyed on
714 * property name).
715 */
716 protected void setupProperties(PropertyDescriptor[] propertyDescriptors) {
717 MetaProperty mp;
718 Method method;
719 MetaMethod getter = null;
720 MetaMethod setter = null;
721 Class klass;
722
723
724 klass = theClass;
725 while(klass != null) {
726 Field[] fields = klass.getDeclaredFields();
727 for(int i = 0; i < fields.length; i++) {
728
729 if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0)
730 continue;
731
732
733 if(propertyMap.get(fields[i].getName()) != null)
734 continue;
735
736
737
738
739 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
740 }
741
742
743 klass = klass.getSuperclass();
744 }
745
746
747 if(theClass.isArray()) {
748 propertyMap.put("length", arrayLengthProperty);
749 }
750
751
752
753 for(int i=0; i<propertyDescriptors.length; i++) {
754 PropertyDescriptor pd = propertyDescriptors[i];
755
756 if(propertyMap.get(pd.getName()) != null)
757 continue;
758
759
760
761
762 if(pd.getPropertyType() == null)
763 continue;
764
765
766 method = pd.getReadMethod();
767 if(method != null)
768 getter = findMethod(method);
769 else
770 getter = null;
771
772
773 method = pd.getWriteMethod();
774 if(method != null)
775 setter = findMethod(method);
776 else
777 setter = null;
778
779
780
781
782
783 mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
784
785
786 propertyMap.put(pd.getName(), mp);
787 }
788
789
790 klass = theClass;
791 while(klass != null) {
792 Method[] methods = klass.getDeclaredMethods();
793 for (int i = 0; i < methods.length; i++) {
794
795 if(Modifier.isPublic(methods[i].getModifiers()) == false)
796 continue;
797
798 method = methods[i];
799
800 String methodName = method.getName();
801
802
803 if(methodName.startsWith("get") &&
804 methodName.length() > 3 &&
805 method.getParameterTypes().length == 0) {
806
807
808 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
809
810
811 mp = (MetaProperty) propertyMap.get(propName);
812 if(mp != null) {
813
814 if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
815
816 ((MetaBeanProperty) mp).setGetter(findMethod(method));
817 }
818 }
819 else {
820
821
822 MetaBeanProperty mbp = new MetaBeanProperty(propName,
823 method.getReturnType(),
824 findMethod(method), null);
825
826
827 propertyMap.put(propName, mbp);
828 }
829 }
830 else if(methodName.startsWith("set") &&
831 methodName.length() > 3 &&
832 method.getParameterTypes().length == 1) {
833
834
835 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
836
837
838 mp = (MetaProperty) propertyMap.get(propName);
839 if(mp != null) {
840 if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
841
842 ((MetaBeanProperty) mp).setSetter(findMethod(method));
843 }
844 }
845 else {
846
847 MetaBeanProperty mbp = new MetaBeanProperty(propName,
848 method.getParameterTypes()[0],
849 null,
850 findMethod(method));
851
852
853 propertyMap.put(propName, mbp);
854 }
855 }
856 }
857
858
859 klass = klass.getSuperclass();
860 }
861 }
862
863 /***
864 * Sets the property value on an object
865 */
866 public void setProperty(Object object, String property, Object newValue) {
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933 MetaProperty mp = (MetaProperty) propertyMap.get(property);
934 if(mp != null) {
935 try {
936 mp.setProperty(object, newValue);
937 return;
938 }
939 catch(ReadOnlyPropertyException e) {
940
941 throw e;
942 }
943 catch (Exception e) {
944
945
946 if (newValue instanceof List) {
947 List list = (List) newValue;
948 int params = list.size();
949 Constructor[] constructors = mp.getType().getConstructors();
950 for (int i = 0; i < constructors.length; i++) {
951 Constructor constructor = constructors[i];
952 if (constructor.getParameterTypes().length == params) {
953 Object value = doConstructorInvoke(constructor, list.toArray());
954 mp.setProperty(object, value);
955 return;
956 }
957 }
958
959
960 Class parameterType = mp.getType();
961 if (parameterType.isArray()) {
962 Object objArray = asPrimitiveArray(list, parameterType);
963 mp.setProperty(object, objArray);
964 return;
965 }
966 }
967
968
969
970
971
972 if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
973 MetaBeanProperty mbp = (MetaBeanProperty) mp;
974 List list = Arrays.asList((Object[])newValue);
975 MetaMethod setter = mbp.getSetter();
976
977 Class parameterType = setter.getParameterTypes()[0];
978 Class arrayType = parameterType.getComponentType();
979 Object objArray = Array.newInstance(arrayType, list.size());
980
981 for (int i = 0; i < list.size(); i++) {
982 List list2 =Arrays.asList((Object[]) list.get(i));
983 Object objArray2 = asPrimitiveArray(list2, arrayType);
984 Array.set(objArray, i, objArray2);
985 }
986
987 doMethodInvoke(object, setter, new Object[]{
988 objArray
989 });
990 return;
991 }
992
993 throw new MissingPropertyException(property, theClass, e);
994 }
995 }
996
997 try {
998 MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
999 if (addListenerMethod != null && newValue instanceof Closure) {
1000
1001 Object proxy =
1002 createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
1003 doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
1004 return;
1005 }
1006
1007 if (genericSetMethod == null) {
1008
1009 List possibleGenericMethods = getMethods("set");
1010 if (possibleGenericMethods != null) {
1011 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
1012 MetaMethod mmethod = (MetaMethod) i.next();
1013 Class[] paramTypes = mmethod.getParameterTypes();
1014 if (paramTypes.length == 2 && paramTypes[0] == String.class) {
1015 Object[] arguments = {property, newValue};
1016 Object answer = doMethodInvoke(object, mmethod, arguments);
1017 return;
1018 }
1019 }
1020 }
1021 }
1022 else {
1023 Object[] arguments = { property, newValue };
1024 doMethodInvoke(object, genericSetMethod, arguments);
1025 return;
1026 }
1027
1028 /*** todo or are we an extensible class? */
1029
1030
1031
1032
1033
1034
1035 String method = "set" + capitalize(property);
1036 try {
1037 invokeMethod(object, method, new Object[] { newValue });
1038 }
1039 catch (MissingMethodException e1) {
1040 try {
1041 Field field = object.getClass().getDeclaredField(property);
1042 field.set(object, newValue);
1043 }
1044 catch (Exception e2) {
1045 throw new MissingPropertyException(property, theClass, e2);
1046 }
1047 }
1048
1049 }
1050 catch (GroovyRuntimeException e) {
1051 throw new MissingPropertyException(property, theClass, e);
1052 }
1053
1054
1055 throw new MissingPropertyException(property, theClass);
1056 }
1057
1058 /***
1059 * @param list
1060 * @param parameterType
1061 * @return
1062 */
1063 private Object asPrimitiveArray(List list, Class parameterType) {
1064 Class arrayType = parameterType.getComponentType();
1065 Object objArray = Array.newInstance(arrayType, list.size());
1066 for (int i = 0; i < list.size(); i++) {
1067 Object obj = list.get(i);
1068 if (arrayType.isPrimitive()) {
1069 if (obj instanceof Integer) {
1070 Array.setInt(objArray, i, ((Integer) obj).intValue());
1071 }
1072 else if (obj instanceof Double) {
1073 Array.setDouble(objArray, i, ((Double) obj).doubleValue());
1074 }
1075 else if (obj instanceof Boolean) {
1076 Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
1077 }
1078 else if (obj instanceof Long) {
1079 Array.setLong(objArray, i, ((Long) obj).longValue());
1080 }
1081 else if (obj instanceof Float) {
1082 Array.setFloat(objArray, i, ((Float) obj).floatValue());
1083 }
1084 else if (obj instanceof Character) {
1085 Array.setChar(objArray, i, ((Character) obj).charValue());
1086 }
1087 else if (obj instanceof Byte) {
1088 Array.setByte(objArray, i, ((Byte) obj).byteValue());
1089 }
1090 else if (obj instanceof Short) {
1091 Array.setShort(objArray, i, ((Short) obj).shortValue());
1092 }
1093 }
1094 else {
1095 Array.set(objArray, i, obj);
1096 }
1097 }
1098 return objArray;
1099 }
1100
1101 public ClassNode getClassNode() {
1102 if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
1103
1104 String className = theClass.getName();
1105 String groovyFile = className;
1106 int idx = groovyFile.indexOf('$');
1107 if (idx > 0) {
1108 groovyFile = groovyFile.substring(0, idx);
1109 }
1110 groovyFile = groovyFile.replace('.', '/') + ".groovy";
1111
1112
1113 URL url = theClass.getClassLoader().getResource(groovyFile);
1114 if (url == null) {
1115 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
1116 }
1117 if (url != null) {
1118 try {
1119
1120 /***
1121 * todo there is no CompileUnit in scope so class name
1122 * checking won't work but that mostly affects the bytecode
1123 * generation rather than viewing the AST
1124 */
1125
1126 CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
1127 public void call( ClassVisitor writer, ClassNode node ) {
1128 if( node.getName().equals(theClass.getName()) ) {
1129 MetaClass.this.classNode = node;
1130 }
1131 }
1132 };
1133
1134
1135 CompilationUnit unit = new CompilationUnit( getClass().getClassLoader() );
1136 unit.setClassgenCallback( search );
1137 unit.addSource( url );
1138 unit.compile( Phases.CLASS_GENERATION );
1139 }
1140 catch (Exception e) {
1141 throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
1142 }
1143 }
1144
1145 }
1146 return classNode;
1147 }
1148
1149 public String toString() {
1150 return super.toString() + "[" + theClass + "]";
1151 }
1152
1153
1154
1155
1156 /***
1157 * Converts the given object into an array; if its an array then just cast
1158 * otherwise wrap it in an array
1159 */
1160 protected Object[] asArray(Object arguments) {
1161 if (arguments == null) {
1162 return EMPTY_ARRAY;
1163 }
1164 if (arguments instanceof Tuple) {
1165 Tuple tuple = (Tuple) arguments;
1166 return tuple.toArray();
1167 }
1168 if (arguments instanceof Object[]) {
1169 return (Object[]) arguments;
1170 }
1171 else {
1172 return new Object[] { arguments };
1173 }
1174 }
1175
1176 /***
1177 * @param listenerType
1178 * the interface of the listener to proxy
1179 * @param listenerMethodName
1180 * the name of the method in the listener API to call the
1181 * closure on
1182 * @param closure
1183 * the closure to invoke on the listenerMethodName method
1184 * invocation
1185 * @return a dynamic proxy which calls the given closure on the given
1186 * method name
1187 */
1188 protected Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
1189 InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
1190 return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
1191 }
1192
1193 /***
1194 * Adds all the methods declared in the given class to the metaclass
1195 * ignoring any matching methods already defined by a derived class
1196 *
1197 * @param theClass
1198 */
1199 protected void addMethods(Class theClass) {
1200 Method[] methodArray = theClass.getDeclaredMethods();
1201 for (int i = 0; i < methodArray.length; i++) {
1202 Method reflectionMethod = methodArray[i];
1203 if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
1204 continue;
1205 }
1206 MetaMethod method = createMetaMethod(reflectionMethod);
1207 addMethod(method);
1208 }
1209 }
1210
1211 protected void addMethod(MetaMethod method) {
1212 String name = method.getName();
1213
1214
1215
1216 if (isGenericGetMethod(method) && genericGetMethod == null) {
1217 genericGetMethod = method;
1218 }
1219 else if (isGenericSetMethod(method) && genericSetMethod == null) {
1220 genericSetMethod = method;
1221 }
1222 if (method.isStatic()) {
1223 List list = (List) staticMethodIndex.get(name);
1224 if (list == null) {
1225 list = new ArrayList();
1226 staticMethodIndex.put(name, list);
1227 list.add(method);
1228 }
1229 else {
1230 if (!containsMatchingMethod(list, method)) {
1231 list.add(method);
1232 }
1233 }
1234 }
1235
1236 List list = (List) methodIndex.get(name);
1237 if (list == null) {
1238 list = new ArrayList();
1239 methodIndex.put(name, list);
1240 list.add(method);
1241 }
1242 else {
1243 if (!containsMatchingMethod(list, method)) {
1244 list.add(method);
1245 }
1246 }
1247 }
1248
1249 /***
1250 * @return true if a method of the same matching prototype was found in the
1251 * list
1252 */
1253 protected boolean containsMatchingMethod(List list, MetaMethod method) {
1254 for (Iterator iter = list.iterator(); iter.hasNext();) {
1255 MetaMethod aMethod = (MetaMethod) iter.next();
1256 Class[] params1 = aMethod.getParameterTypes();
1257 Class[] params2 = method.getParameterTypes();
1258 if (params1.length == params2.length) {
1259 boolean matches = true;
1260 for (int i = 0; i < params1.length; i++) {
1261 if (params1[i] != params2[i]) {
1262 matches = false;
1263 break;
1264 }
1265 }
1266 if (matches) {
1267 return true;
1268 }
1269 }
1270 }
1271 return false;
1272 }
1273
1274 /***
1275 * Adds all of the newly defined methods from the given class to this
1276 * metaclass
1277 *
1278 * @param theClass
1279 */
1280 protected void addNewStaticMethodsFrom(Class theClass) {
1281 MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
1282 Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
1283 while (iter.hasNext()) {
1284 MetaMethod method = (MetaMethod) iter.next();
1285 addMethod(method);
1286 newGroovyMethodsList.add(method);
1287 }
1288 }
1289
1290 /***
1291 * @return the value of the static property of the given class
1292 */
1293 protected Object getStaticProperty(Class aClass, String property) {
1294
1295
1296
1297 Exception lastException = null;
1298 try {
1299 Field field = aClass.getField(property);
1300 if (field != null) {
1301 if ((field.getModifiers() & Modifier.STATIC) != 0) {
1302 return field.get(null);
1303 }
1304 }
1305 }
1306 catch (Exception e) {
1307 lastException = e;
1308 }
1309
1310
1311 try {
1312 MetaMethod method = findStaticGetter(aClass, "get" + capitalize(property));
1313 if (method != null) {
1314 return doMethodInvoke(aClass, method, EMPTY_ARRAY);
1315 }
1316 }
1317 catch (GroovyRuntimeException e) {
1318 throw new MissingPropertyException(property, aClass, e);
1319 }
1320
1321 if (lastException == null) {
1322 throw new MissingPropertyException(property, aClass);
1323 }
1324 else {
1325 throw new MissingPropertyException(property, aClass, lastException);
1326 }
1327 }
1328
1329 /***
1330 * @return the matching method which should be found
1331 */
1332 protected MetaMethod findMethod(Method aMethod) {
1333 List methods = getMethods(aMethod.getName());
1334 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1335 MetaMethod method = (MetaMethod) iter.next();
1336 if (method.isMethod(aMethod)) {
1337 return method;
1338 }
1339 }
1340
1341 return new ReflectionMetaMethod(aMethod);
1342 }
1343
1344 /***
1345 * @return the getter method for the given object
1346 */
1347 protected MetaMethod findGetter(Object object, String name) {
1348 List methods = getMethods(name);
1349 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1350 MetaMethod method = (MetaMethod) iter.next();
1351 if (method.getParameterTypes().length == 0) {
1352 return method;
1353 }
1354 }
1355 return null;
1356 }
1357
1358 /***
1359 * @return the Method of the given name with no parameters or null
1360 */
1361 protected MetaMethod findStaticGetter(Class type, String name) {
1362 List methods = getStaticMethods(name);
1363 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1364 MetaMethod method = (MetaMethod) iter.next();
1365 if (method.getParameterTypes().length == 0) {
1366 return method;
1367 }
1368 }
1369
1370 /*** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
1371 try {
1372 Method method = type.getMethod(name, EMPTY_TYPE_ARRAY);
1373 if ((method.getModifiers() & Modifier.STATIC) != 0) {
1374 return findMethod(method);
1375 }
1376 else {
1377 return null;
1378 }
1379 }
1380 catch (Exception e) {
1381 return null;
1382 }
1383 }
1384
1385 protected Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
1386
1387
1388
1389
1390
1391 try {
1392 if (argumentArray == null) {
1393 argumentArray = EMPTY_ARRAY;
1394 }
1395 else if (method.getParameterTypes().length == 1 && argumentArray.length == 0) {
1396 argumentArray = ARRAY_WITH_NULL;
1397 }
1398 return method.invoke(object, argumentArray);
1399 }
1400 catch (ClassCastException e) {
1401 if (coerceGStrings(argumentArray)) {
1402 try {
1403 return doMethodInvoke(object, method, argumentArray);
1404 }
1405 catch (Exception e2) {
1406
1407 }
1408 }
1409 throw new GroovyRuntimeException(
1410 "failed to invoke method: "
1411 + method
1412 + " on: "
1413 + object
1414 + " with arguments: "
1415 + InvokerHelper.toString(argumentArray)
1416 + " reason: "
1417 + e,
1418 e);
1419 }
1420 catch (InvocationTargetException e) {
1421 Throwable t = e.getTargetException();
1422 if (t instanceof Error) {
1423 Error error = (Error) t;
1424 throw error;
1425 }
1426 if (t instanceof RuntimeException) {
1427 RuntimeException runtimeEx = (RuntimeException) t;
1428 throw runtimeEx;
1429 }
1430 throw new InvokerInvocationException(e);
1431 }
1432 catch (IllegalAccessException e) {
1433 throw new GroovyRuntimeException(
1434 "could not access method: "
1435 + method
1436 + " on: "
1437 + object
1438 + " with arguments: "
1439 + InvokerHelper.toString(argumentArray)
1440 + " reason: "
1441 + e,
1442 e);
1443 }
1444 catch (IllegalArgumentException e) {
1445 if (coerceGStrings(argumentArray)) {
1446 try {
1447 return doMethodInvoke(object, method, argumentArray);
1448 }
1449 catch (Exception e2) {
1450
1451 }
1452 }
1453 Object[] args = coerceNumbers(method, argumentArray);
1454 if (args != null) {
1455 try {
1456 return doMethodInvoke(object, method, args);
1457 }
1458 catch (Exception e3) {
1459
1460 }
1461 }
1462
1463 throw new GroovyRuntimeException(
1464 "failed to invoke method: "
1465 + method
1466 + " on: "
1467 + object
1468 + " with arguments: "
1469 + InvokerHelper.toString(argumentArray)
1470 + " reason: "
1471 + e,
1472 e);
1473 }
1474 catch (RuntimeException e) {
1475 throw e;
1476 }
1477 catch (Exception e) {
1478 throw new GroovyRuntimeException(
1479 "failed to invoke method: "
1480 + method
1481 + " on: "
1482 + object
1483 + " with arguments: "
1484 + InvokerHelper.toString(argumentArray)
1485 + " reason: "
1486 + e,
1487 e);
1488 }
1489 }
1490
1491 private static Object[] coerceNumbers(MetaMethod method, Object[] arguments) {
1492 Object[] ans = null;
1493 boolean coerced = false;
1494
1495 Class[] params = method.getParameterTypes();
1496
1497 if (params.length != arguments.length) {
1498 return null;
1499 }
1500
1501 ans = new Object[arguments.length];
1502
1503 for (int i = 0, size = arguments.length; i < size; i++) {
1504 Object argument = arguments[i];
1505 Class param = params[i];
1506 if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) {
1507 if (param == Byte.class || param == Byte.TYPE ) {
1508 ans[i] = new Byte(((Number)argument).byteValue());
1509 coerced = true; continue;
1510 }
1511 if (param == Double.class || param == Double.TYPE) {
1512 ans[i] = new Double(((Number)argument).doubleValue());
1513 coerced = true; continue;
1514 }
1515 if (param == Float.class || param == Float.TYPE) {
1516 ans[i] = new Float(((Number)argument).floatValue());
1517 coerced = true; continue;
1518 }
1519 if (param == Integer.class || param == Integer.TYPE) {
1520 ans[i] = new Integer(((Number)argument).intValue());
1521 coerced = true; continue;
1522 }
1523 if (param == Long.class || param == Long.TYPE) {
1524 ans[i] = new Long(((Number)argument).longValue());
1525 coerced = true; continue;
1526 }
1527 if (param == Short.class || param == Short.TYPE) {
1528 ans[i] = new Short(((Number)argument).shortValue());
1529 coerced = true; continue;
1530 }
1531 if (param == BigDecimal.class ) {
1532 ans[i] = new BigDecimal(((Number)argument).doubleValue());
1533 coerced = true; continue;
1534 }
1535 if (param == BigInteger.class) {
1536 ans[i] = new BigInteger(String.valueOf(((Number)argument).longValue()));
1537 coerced = true; continue;
1538 }
1539 }
1540 else if (param.isArray() && argument.getClass().isArray()) {
1541 Class paramElem = param.getComponentType();
1542 if (paramElem.isPrimitive()) {
1543 if (paramElem == boolean.class && argument.getClass().getName().equals("[Ljava.lang.Boolean;")) {
1544 ans[i] = InvokerHelper.convertToBooleanArray(argument);
1545 coerced = true;
1546 continue;
1547 }
1548 if (paramElem == byte.class && argument.getClass().getName().equals("[Ljava.lang.Byte;")) {
1549 ans[i] = InvokerHelper.convertToByteArray(argument);
1550 coerced = true;
1551 continue;
1552 }
1553 if (paramElem == char.class && argument.getClass().getName().equals("[Ljava.lang.Character;")) {
1554 ans[i] = InvokerHelper.convertToCharArray(argument);
1555 coerced = true;
1556 continue;
1557 }
1558 if (paramElem == short.class && argument.getClass().getName().equals("[Ljava.lang.Short;")) {
1559 ans[i] = InvokerHelper.convertToShortArray(argument);
1560 coerced = true;
1561 continue;
1562 }
1563 if (paramElem == int.class && argument.getClass().getName().equals("[Ljava.lang.Integer;")) {
1564 ans[i] = InvokerHelper.convertToIntArray(argument);
1565 coerced = true;
1566 continue;
1567 }
1568 if (paramElem == long.class
1569 && argument.getClass().getName().equals("[Ljava.lang.Long;")
1570 && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1571 ) {
1572 ans[i] = InvokerHelper.convertToLongArray(argument);
1573 coerced = true;
1574 continue;
1575 }
1576 if (paramElem == float.class
1577 && argument.getClass().getName().equals("[Ljava.lang.Float;")
1578 && argument.getClass().getName().equals("[Ljava.lang.Integer;")
1579 ) {
1580 ans[i] = InvokerHelper.convertToFloatArray(argument);
1581 coerced = true;
1582 continue;
1583 }
1584 if (paramElem == double.class &&
1585 argument.getClass().getName().equals("[Ljava.lang.Double;") &&
1586 argument.getClass().getName().equals("[Ljava.lang.BigDecimal;") &&
1587 argument.getClass().getName().equals("[Ljava.lang.Float;")) {
1588 ans[i] = InvokerHelper.convertToDoubleArray(argument);
1589 coerced = true;
1590 continue;
1591 }
1592 }
1593 }
1594 }
1595 return coerced ? ans : null;
1596 }
1597
1598 protected Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
1599
1600
1601
1602
1603 try {
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616 return constructor.newInstance(argumentArray);
1617 }
1618 catch (InvocationTargetException e) {
1619 Throwable t = e.getTargetException();
1620 if (t instanceof Error) {
1621 Error error = (Error) t;
1622 throw error;
1623 }
1624 if (t instanceof RuntimeException) {
1625 RuntimeException runtimeEx = (RuntimeException) t;
1626 throw runtimeEx;
1627 }
1628 throw new InvokerInvocationException(e);
1629 }
1630 catch (IllegalArgumentException e) {
1631 if (coerceGStrings(argumentArray)) {
1632 try {
1633 return constructor.newInstance(argumentArray);
1634 }
1635 catch (Exception e2) {
1636
1637 }
1638 }
1639 throw new GroovyRuntimeException(
1640 "failed to invoke constructor: "
1641 + constructor
1642 + " with arguments: "
1643 + InvokerHelper.toString(argumentArray)
1644 + " reason: "
1645 + e,
1646 e);
1647 }
1648 catch (IllegalAccessException e) {
1649 throw new GroovyRuntimeException(
1650 "could not access constructor: "
1651 + constructor
1652 + " with arguments: "
1653 + InvokerHelper.toString(argumentArray)
1654 + " reason: "
1655 + e,
1656 e);
1657 }
1658 catch (Exception e) {
1659 throw new GroovyRuntimeException(
1660 "failed to invoke constructor: "
1661 + constructor
1662 + " with arguments: "
1663 + InvokerHelper.toString(argumentArray)
1664 + " reason: "
1665 + e,
1666 e);
1667 }
1668 }
1669
1670 /***
1671 * Chooses the correct method to use from a list of methods which match by
1672 * name.
1673 *
1674 * @param methods
1675 * the possible methods to choose from
1676 * @param arguments
1677 * the original argument to the method
1678 * @return
1679 */
1680 protected Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
1681 int methodCount = methods.size();
1682 if (methodCount <= 0) {
1683 return null;
1684 }
1685 else if (methodCount == 1) {
1686 Object method = methods.get(0);
1687 if (isValidMethod(method, arguments, coerce)) {
1688 return method;
1689 }
1690 return null;
1691 }
1692 Object answer = null;
1693 if (arguments == null || arguments.length == 0) {
1694 answer = chooseEmptyMethodParams(methods);
1695 }
1696 else if (arguments.length == 1 && arguments[0] == null) {
1697 answer = chooseMostGeneralMethodWith1Param(methods);
1698 }
1699 else {
1700 List matchingMethods = new ArrayList();
1701
1702 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1703 Object method = iter.next();
1704 Class[] paramTypes;
1705
1706
1707 if (isValidMethod(method, arguments, coerce)) {
1708 matchingMethods.add(method);
1709 }
1710 }
1711 if (matchingMethods.isEmpty()) {
1712 return null;
1713 }
1714 else if (matchingMethods.size() == 1) {
1715 return matchingMethods.get(0);
1716 }
1717 return chooseMostSpecificParams(methodName, matchingMethods, arguments);
1718
1719 }
1720 if (answer != null) {
1721 return answer;
1722 }
1723 throw new GroovyRuntimeException(
1724 "Could not find which method to invoke from this list: "
1725 + methods
1726 + " for arguments: "
1727 + InvokerHelper.toString(arguments));
1728 }
1729
1730 protected boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
1731 Class[] paramTypes = getParameterTypes(method);
1732 return isValidMethod(paramTypes, arguments, includeCoerce);
1733 }
1734
1735 public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
1736 if (arguments == null) {
1737 return true;
1738 }
1739 int size = arguments.length;
1740 boolean validMethod = false;
1741 if (paramTypes.length == size) {
1742
1743 validMethod = true;
1744 for (int i = 0; i < size; i++) {
1745 if (!isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) {
1746 validMethod = false;
1747 }
1748 }
1749 }
1750 else {
1751 if (paramTypes.length == 1 && size == 0) {
1752 return true;
1753 }
1754 }
1755 return validMethod;
1756 }
1757
1758 protected Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
1759 Object answer = null;
1760 int size = arguments.length;
1761 Class[] mostSpecificTypes = null;
1762 for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
1763 Object method = iter.next();
1764 Class[] paramTypes = getParameterTypes(method);
1765 if (answer == null) {
1766 answer = method;
1767 mostSpecificTypes = paramTypes;
1768 }
1769 else {
1770 boolean useThisMethod = false;
1771 for (int i = 0; i < size; i++) {
1772 Class mostSpecificType = mostSpecificTypes[i];
1773 Class type = paramTypes[i];
1774
1775 if (!isAssignableFrom(mostSpecificType, type)) {
1776
1777 useThisMethod = true;
1778 break;
1779 }
1780
1781 }
1782 if (useThisMethod) {
1783
1784 if (size > 1) {
1785 checkForInvalidOverloading(name, mostSpecificTypes, paramTypes);
1786 }
1787
1788 answer = method;
1789 mostSpecificTypes = paramTypes;
1790 }
1791 }
1792 }
1793 return answer;
1794 }
1795
1796 /***
1797 * Checks that one of the parameter types is a superset of the other and
1798 * that the two lists of types don't conflict. e.g. foo(String, Object) and
1799 * foo(Object, String) would conflict if called with foo("a", "b").
1800 *
1801 * Note that this method is only called with 2 possible signatures. i.e.
1802 * possible invalid combinations will already have been filtered out. So if
1803 * there were methods foo(String, Object) and foo(Object, String) then one
1804 * of these would be already filtered out if foo was called as foo(12, "a")
1805 */
1806 protected void checkForInvalidOverloading(String name, Class[] baseTypes, Class[] derivedTypes) {
1807 for (int i = 0, size = baseTypes.length; i < size; i++) {
1808 Class baseType = baseTypes[i];
1809 Class derivedType = derivedTypes[i];
1810 if (!isAssignableFrom(derivedType, baseType)) {
1811 throw new GroovyRuntimeException(
1812 "Ambiguous method overloading for method: "
1813 + name
1814 + ". Cannot resolve which method to invoke due to overlapping prototypes between: "
1815 + InvokerHelper.toString(baseTypes)
1816 + " and: "
1817 + InvokerHelper.toString(derivedTypes));
1818 }
1819 }
1820 }
1821
1822 protected Class[] getParameterTypes(Object methodOrConstructor) {
1823 if (methodOrConstructor instanceof MetaMethod) {
1824 MetaMethod method = (MetaMethod) methodOrConstructor;
1825 return method.getParameterTypes();
1826 }
1827 if (methodOrConstructor instanceof Method) {
1828 Method method = (Method) methodOrConstructor;
1829 return method.getParameterTypes();
1830 }
1831 if (methodOrConstructor instanceof Constructor) {
1832 Constructor constructor = (Constructor) methodOrConstructor;
1833 return constructor.getParameterTypes();
1834 }
1835 throw new IllegalArgumentException("Must be a Method or Constructor");
1836 }
1837
1838 /***
1839 * @return the method with 1 parameter which takes the most general type of
1840 * object (e.g. Object)
1841 */
1842 protected Object chooseMostGeneralMethodWith1Param(List methods) {
1843
1844
1845 Class closestClass = null;
1846 Object answer = null;
1847
1848 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1849 Object method = iter.next();
1850 Class[] paramTypes = getParameterTypes(method);
1851 int paramLength = paramTypes.length;
1852 if (paramLength == 1) {
1853 Class theType = paramTypes[0];
1854 if (closestClass == null || isAssignableFrom(closestClass, theType)) {
1855 closestClass = theType;
1856 answer = method;
1857 }
1858 }
1859 }
1860 return answer;
1861 }
1862
1863 /***
1864 * @return the method with 1 parameter which takes the most general type of
1865 * object (e.g. Object)
1866 */
1867 protected Object chooseEmptyMethodParams(List methods) {
1868 for (Iterator iter = methods.iterator(); iter.hasNext();) {
1869 Object method = iter.next();
1870 Class[] paramTypes = getParameterTypes(method);
1871 int paramLength = paramTypes.length;
1872 if (paramLength == 0) {
1873 return method;
1874 }
1875 }
1876 return null;
1877 }
1878
1879 protected static boolean isCompatibleInstance(Class type, Object value, boolean includeCoerce) {
1880 boolean answer = value == null || type.isInstance(value);
1881 if (!answer) {
1882 if (type.isPrimitive()) {
1883 if (type == int.class) {
1884 return value instanceof Integer;
1885 }
1886 else if (type == double.class) {
1887 return value instanceof Double || value instanceof Float || value instanceof Integer || value instanceof BigDecimal;
1888 }
1889 else if (type == boolean.class) {
1890 return value instanceof Boolean;
1891 }
1892 else if (type == long.class) {
1893 return value instanceof Long || value instanceof Integer;
1894 }
1895 else if (type == float.class) {
1896 return value instanceof Float || value instanceof Integer;
1897 }
1898 else if (type == char.class) {
1899 return value instanceof Character;
1900 }
1901 else if (type == byte.class) {
1902 return value instanceof Byte;
1903 }
1904 else if (type == short.class) {
1905 return value instanceof Short;
1906 }
1907 }
1908 else if(type.isArray() && value.getClass().isArray()) {
1909 return isCompatibleClass(type.getComponentType(), value.getClass().getComponentType(), false);
1910 }
1911 else if (includeCoerce) {
1912 if (type == String.class && value instanceof GString) {
1913 return true;
1914 }
1915 else if (value instanceof Number) {
1916
1917 return Number.class.isAssignableFrom(type);
1918 }
1919 }
1920 }
1921 return answer;
1922 }
1923 protected static boolean isCompatibleClass(Class type, Class value, boolean includeCoerce) {
1924 boolean answer = value == null || type.isAssignableFrom(value);
1925 if (!answer) {
1926 if (type.isPrimitive()) {
1927 if (type == int.class) {
1928 return value == Integer.class;
1929 }
1930 else if (type == double.class) {
1931 return value == Double.class || value == Float.class || value == Integer.class || value == BigDecimal.class;
1932 }
1933 else if (type == boolean.class) {
1934 return value == Boolean.class;
1935 }
1936 else if (type == long.class) {
1937 return value == Long.class || value == Integer.class;
1938 }
1939 else if (type == float.class) {
1940 return value == Float.class || value == Integer.class;
1941 }
1942 else if (type == char.class) {
1943 return value == Character.class;
1944 }
1945 else if (type == byte.class) {
1946 return value == Byte.class;
1947 }
1948 else if (type == short.class) {
1949 return value == Short.class;
1950 }
1951 }
1952 else if(type.isArray() && value.isArray()) {
1953 return isCompatibleClass(type.getComponentType(), value.getComponentType(), false);
1954 }
1955 else if (includeCoerce) {
1956
1957 if (type == String.class && GString.class.isAssignableFrom(value)) {
1958 return true;
1959 }
1960 else if (value == Number.class) {
1961
1962 return Number.class.isAssignableFrom(type);
1963 }
1964 }
1965 }
1966 return answer;
1967 }
1968
1969 protected boolean isAssignableFrom(Class mostSpecificType, Class type) {
1970
1971 if (mostSpecificType.isPrimitive() && type.isPrimitive()) {
1972 if (mostSpecificType == type) {
1973 return true;
1974 }
1975 else {
1976 if (type == int.class) {
1977 return
1978 mostSpecificType == int.class
1979 || mostSpecificType == short.class
1980 || mostSpecificType == byte.class;
1981 }
1982 else if (type == double.class) {
1983 return
1984 mostSpecificType == double.class
1985 || mostSpecificType == int.class
1986 || mostSpecificType == long.class
1987 || mostSpecificType == short.class
1988 || mostSpecificType == byte.class
1989 || mostSpecificType == float.class;
1990 }
1991 else if (type == long.class) {
1992 return
1993 mostSpecificType == long.class
1994 || mostSpecificType == int.class
1995 || mostSpecificType == short.class
1996 || mostSpecificType == byte.class;
1997 }
1998 else if (type == float.class) {
1999 return
2000 mostSpecificType == float.class
2001 || mostSpecificType == int.class
2002 || mostSpecificType == long.class
2003 || mostSpecificType == short.class
2004 || mostSpecificType == byte.class;
2005 }
2006 else if (type == short.class) {
2007 return
2008 mostSpecificType == short.class
2009 || mostSpecificType == byte.class;
2010 }
2011 else {
2012 return false;
2013 }
2014 }
2015 }
2016
2017 boolean answer = type.isAssignableFrom(mostSpecificType);
2018 if (!answer) {
2019 answer = autoboxType(type).isAssignableFrom(autoboxType(mostSpecificType));
2020 }
2021 return answer;
2022 }
2023
2024 private Class autoboxType(Class type) {
2025 if (type.isPrimitive()) {
2026 if (type == int.class) {
2027 return Integer.class;
2028 }
2029 else if (type == double.class) {
2030 return Double.class;
2031 }
2032 else if (type == long.class) {
2033 return Long.class;
2034 }
2035 else if (type == boolean.class) {
2036 return Boolean.class;
2037 }
2038 else if (type == float.class) {
2039 return Float.class;
2040 }
2041 else if (type == char.class) {
2042 return Character.class;
2043 }
2044 else if (type == byte.class) {
2045 return Byte.class;
2046 }
2047 else if (type == short.class) {
2048 return Short.class;
2049 }
2050 }
2051 return type;
2052 }
2053
2054 /***
2055 * Coerces any GString instances into Strings
2056 *
2057 * @return true if some coercion was done.
2058 */
2059 protected static boolean coerceGStrings(Object[] arguments) {
2060 boolean coerced = false;
2061 for (int i = 0, size = arguments.length; i < size; i++) {
2062 Object argument = arguments[i];
2063 if (argument instanceof GString) {
2064 arguments[i] = argument.toString();
2065 coerced = true;
2066 }
2067 }
2068 return coerced;
2069 }
2070
2071 protected boolean isGenericSetMethod(MetaMethod method) {
2072 return (method.getName().equals("set"))
2073 && method.getParameterTypes().length == 2;
2074 }
2075
2076 protected boolean isGenericGetMethod(MetaMethod method) {
2077 if (method.getName().equals("get")) {
2078 Class[] parameterTypes = method.getParameterTypes();
2079 return parameterTypes.length == 1 && parameterTypes[0] == String.class;
2080 }
2081 return false;
2082 }
2083
2084 private void registerMethods(boolean instanceMethods) {
2085 Method[] methods = theClass.getMethods();
2086 for (int i = 0; i < methods.length; i++) {
2087 Method method = methods[i];
2088 if (MethodHelper.isStatic(method)) {
2089 Class[] paramTypes = method.getParameterTypes();
2090 if (paramTypes.length > 0) {
2091 Class owner = paramTypes[0];
2092 if (instanceMethods) {
2093 registry.lookup(owner).addNewInstanceMethod(method);
2094 } else {
2095 registry.lookup(owner).addNewStaticMethod(method);
2096 }
2097 }
2098 }
2099 }
2100 }
2101
2102 protected void registerStaticMethods() {
2103 registerMethods(false);
2104 }
2105
2106 protected void registerInstanceMethods() {
2107 registerMethods(true);
2108 }
2109
2110 protected String capitalize(String property) {
2111 return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
2112 }
2113
2114 /***
2115 * Call this method when any mutation method is called, such as adding a new
2116 * method to this MetaClass so that any caching or bytecode generation can be
2117 * regenerated.
2118 */
2119 protected synchronized void onMethodChange() {
2120 reflector = null;
2121 }
2122
2123 protected synchronized void checkInitialised() {
2124 if (!initialised) {
2125 initialised = true;
2126 addInheritedMethods(theClass);
2127 }
2128 if (reflector == null) {
2129 generateReflector();
2130 }
2131 }
2132
2133 protected MetaMethod createMetaMethod(final Method method) {
2134 if (registry.useAccessible()) {
2135 AccessController.doPrivileged(new PrivilegedAction() {
2136 public Object run() {
2137 method.setAccessible(true);
2138 return null;
2139 }
2140 });
2141 }
2142 if (useReflection) {
2143
2144 return new ReflectionMetaMethod(method);
2145 }
2146 MetaMethod answer = new MetaMethod(method);
2147 if (isValidReflectorMethod(answer)) {
2148 allMethods.add(answer);
2149 answer.setMethodIndex(allMethods.size());
2150 }
2151 else {
2152
2153 answer = new ReflectionMetaMethod(method);
2154 }
2155 return answer;
2156 }
2157
2158 protected boolean isValidReflectorMethod(MetaMethod method) {
2159
2160 if (!method.isPublic()) {
2161 return false;
2162 }
2163 Class declaringClass = method.getDeclaringClass();
2164 if (!Modifier.isPublic(declaringClass.getModifiers())) {
2165
2166 List list = getInterfaceMethods();
2167 for (Iterator iter = list.iterator(); iter.hasNext();) {
2168 MetaMethod aMethod = (MetaMethod) iter.next();
2169 if (method.isSame(aMethod)) {
2170 method.setInterfaceClass(aMethod.getDeclaringClass());
2171 return true;
2172 }
2173 }
2174 /*** todo */
2175
2176 return false;
2177 }
2178 return true;
2179 }
2180
2181 protected void generateReflector() {
2182 reflector = loadReflector(allMethods);
2183 if (reflector == null) {
2184 throw new RuntimeException("Should have a reflector!");
2185 }
2186
2187 for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
2188 MetaMethod metaMethod = (MetaMethod) iter.next();
2189
2190 metaMethod.setReflector(reflector);
2191 }
2192 }
2193
2194 protected Reflector loadReflector(List methods) {
2195 ReflectorGenerator generator = new ReflectorGenerator(methods);
2196 String className = theClass.getName();
2197 String packagePrefix = "gjdk.";
2198
2199
2200
2201
2202
2203 String name = packagePrefix + className + "_GroovyReflector";
2204 if (theClass.isArray()) {
2205 String componentName = theClass.getComponentType().getName();
2206
2207
2208
2209
2210
2211 name = packagePrefix + componentName + "_GroovyReflectorArray";
2212 }
2213
2214 try {
2215 Class type = loadReflectorClass(name);
2216 return (Reflector) type.newInstance();
2217 }
2218 catch (AccessControlException ace) {
2219
2220 throw ace;
2221 }
2222 catch (Exception e) {
2223
2224 }
2225
2226 ClassWriter cw = new ClassWriter(true);
2227 generator.generate(cw, name);
2228
2229 byte[] bytecode = cw.toByteArray();
2230
2231 try {
2232 Class type = loadReflectorClass(name, bytecode);
2233 return (Reflector) type.newInstance();
2234 }
2235 catch (Exception e) {
2236 throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + e, e);
2237 }
2238 }
2239
2240 protected Class loadReflectorClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
2241 ClassLoader loader = theClass.getClassLoader();
2242 if (loader instanceof GroovyClassLoader) {
2243 final GroovyClassLoader gloader = (GroovyClassLoader) loader;
2244 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
2245 public Object run() {
2246 return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
2247 }
2248 });
2249 }
2250 return registry.loadClass(name, bytecode);
2251 }
2252
2253 protected Class loadReflectorClass(String name) throws ClassNotFoundException {
2254 ClassLoader loader = theClass.getClassLoader();
2255 if (loader instanceof GroovyClassLoader) {
2256 GroovyClassLoader gloader = (GroovyClassLoader) loader;
2257 return gloader.loadClass(name);
2258 }
2259 return registry.loadClass(name);
2260 }
2261
2262 public List getMethods() {
2263 return allMethods;
2264 }
2265
2266 public List getMetaMethods() {
2267 return (List) ((ArrayList)newGroovyMethodsList).clone();
2268 }
2269
2270 protected synchronized List getInterfaceMethods() {
2271 if (interfaceMethods == null) {
2272 interfaceMethods = new ArrayList();
2273 Class type = theClass;
2274 while (type != null) {
2275 Class[] interfaces = type.getInterfaces();
2276 for (int i = 0; i < interfaces.length; i++) {
2277 Class iface = interfaces[i];
2278 Method[] methods = iface.getMethods();
2279 addInterfaceMethods(interfaceMethods, methods);
2280 }
2281 type = type.getSuperclass();
2282 }
2283 }
2284 return interfaceMethods;
2285 }
2286
2287 private void addInterfaceMethods(List list, Method[] methods) {
2288 for (int i = 0; i < methods.length; i++) {
2289 list.add(createMetaMethod(methods[i]));
2290 }
2291 }
2292
2293 /***
2294 * param instance array to the type array
2295 * @param args
2296 * @return
2297 */
2298 Class[] convertToTypeArray(Object[] args) {
2299 if (args == null)
2300 return null;
2301 int s = args.length;
2302 Class[] ans = new Class[s];
2303 for (int i = 0; i < s; i++) {
2304 Object o = args[i];
2305 if (o != null) {
2306 ans[i] = o.getClass();
2307 } else {
2308 ans[i] = null;
2309 }
2310 }
2311 return ans;
2312 }
2313 }