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