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