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