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