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