view src/clojure/lang/Reflector.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
1 /**
2 * Copyright (c) Rich Hickey. All rights reserved.
3 * The use and distribution terms for this software are covered by the
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 * which can be found in the file epl-v10.html at the root of this distribution.
6 * By using this software in any fashion, you are agreeing to be bound by
7 * the terms of this license.
8 * You must not remove this notice, or any other, from this software.
9 **/
11 /* rich Apr 19, 2006 */
13 package clojure.lang;
15 import java.lang.reflect.*;
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Arrays;
21 public class Reflector{
23 public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) throws Exception{
24 try
25 {
26 Class c = target.getClass();
27 List methods = getMethods(c, args.length, methodName, false);
28 return invokeMatchingMethod(methodName, methods, target, args);
29 }
30 catch(InvocationTargetException e)
31 {
32 if(e.getCause() instanceof Exception)
33 throw (Exception) e.getCause();
34 else if(e.getCause() instanceof Error)
35 throw (Error) e.getCause();
36 throw e;
37 }
38 }
40 private static String noMethodReport(String methodName, Object target){
41 return "No matching method found: " + methodName
42 + (target==null?"":" for " + target.getClass());
43 }
44 static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args)
45 throws Exception{
46 Method m = null;
47 Object[] boxedArgs = null;
48 if(methods.isEmpty())
49 {
50 throw new IllegalArgumentException(noMethodReport(methodName,target));
51 }
52 else if(methods.size() == 1)
53 {
54 m = (Method) methods.get(0);
55 boxedArgs = boxArgs(m.getParameterTypes(), args);
56 }
57 else //overloaded w/same arity
58 {
59 Method foundm = null;
60 for(Iterator i = methods.iterator(); i.hasNext();)
61 {
62 m = (Method) i.next();
64 Class[] params = m.getParameterTypes();
65 if(isCongruent(params, args))
66 {
67 if(foundm == null || Compiler.subsumes(params, foundm.getParameterTypes()))
68 {
69 foundm = m;
70 boxedArgs = boxArgs(params, args);
71 }
72 }
73 }
74 m = foundm;
75 }
76 if(m == null)
77 throw new IllegalArgumentException(noMethodReport(methodName,target));
79 if(!Modifier.isPublic(m.getDeclaringClass().getModifiers()))
80 {
81 //public method of non-public class, try to find it in hierarchy
82 Method oldm = m;
83 m = getAsMethodOfPublicBase(m.getDeclaringClass(), m);
84 if(m == null)
85 throw new IllegalArgumentException("Can't call public method of non-public class: " +
86 oldm.toString());
87 }
88 try
89 {
90 return prepRet(m.invoke(target, boxedArgs));
91 }
92 catch(InvocationTargetException e)
93 {
94 if(e.getCause() instanceof Exception)
95 throw (Exception) e.getCause();
96 else if(e.getCause() instanceof Error)
97 throw (Error) e.getCause();
98 throw e;
99 }
101 }
103 public static Method getAsMethodOfPublicBase(Class c, Method m){
104 for(Class iface : c.getInterfaces())
105 {
106 for(Method im : iface.getMethods())
107 {
108 if(im.getName().equals(m.getName())
109 && Arrays.equals(m.getParameterTypes(), im.getParameterTypes()))
110 {
111 return im;
112 }
113 }
114 }
115 Class sc = c.getSuperclass();
116 if(sc == null)
117 return null;
118 for(Method scm : sc.getMethods())
119 {
120 if(scm.getName().equals(m.getName())
121 && Arrays.equals(m.getParameterTypes(), scm.getParameterTypes())
122 && Modifier.isPublic(scm.getDeclaringClass().getModifiers()))
123 {
124 return scm;
125 }
126 }
127 return getAsMethodOfPublicBase(sc, m);
128 }
130 public static Object invokeConstructor(Class c, Object[] args) throws Exception{
131 try
132 {
133 Constructor[] allctors = c.getConstructors();
134 ArrayList ctors = new ArrayList();
135 for(int i = 0; i < allctors.length; i++)
136 {
137 Constructor ctor = allctors[i];
138 if(ctor.getParameterTypes().length == args.length)
139 ctors.add(ctor);
140 }
141 if(ctors.isEmpty())
142 {
143 throw new IllegalArgumentException("No matching ctor found"
144 + " for " + c);
145 }
146 else if(ctors.size() == 1)
147 {
148 Constructor ctor = (Constructor) ctors.get(0);
149 return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args));
150 }
151 else //overloaded w/same arity
152 {
153 for(Iterator iterator = ctors.iterator(); iterator.hasNext();)
154 {
155 Constructor ctor = (Constructor) iterator.next();
156 Class[] params = ctor.getParameterTypes();
157 if(isCongruent(params, args))
158 {
159 Object[] boxedArgs = boxArgs(params, args);
160 return ctor.newInstance(boxedArgs);
161 }
162 }
163 throw new IllegalArgumentException("No matching ctor found"
164 + " for " + c);
165 }
166 }
167 catch(InvocationTargetException e)
168 {
169 if(e.getCause() instanceof Exception)
170 throw (Exception) e.getCause();
171 else if(e.getCause() instanceof Error)
172 throw (Error) e.getCause();
173 throw e;
174 }
175 }
177 public static Object invokeStaticMethodVariadic(String className, String methodName, Object... args) throws Exception{
178 return invokeStaticMethod(className, methodName, args);
180 }
182 public static Object invokeStaticMethod(String className, String methodName, Object[] args) throws Exception{
183 Class c = RT.classForName(className);
184 try
185 {
186 return invokeStaticMethod(c, methodName, args);
187 }
188 catch(InvocationTargetException e)
189 {
190 if(e.getCause() instanceof Exception)
191 throw (Exception) e.getCause();
192 else if(e.getCause() instanceof Error)
193 throw (Error) e.getCause();
194 throw e;
195 }
196 }
198 public static Object invokeStaticMethod(Class c, String methodName, Object[] args) throws Exception{
199 if(methodName.equals("new"))
200 return invokeConstructor(c, args);
201 List methods = getMethods(c, args.length, methodName, true);
202 return invokeMatchingMethod(methodName, methods, null, args);
203 }
205 public static Object getStaticField(String className, String fieldName) throws Exception{
206 Class c = RT.classForName(className);
207 return getStaticField(c, fieldName);
208 }
210 public static Object getStaticField(Class c, String fieldName) throws Exception{
211 // if(fieldName.equals("class"))
212 // return c;
213 Field f = getField(c, fieldName, true);
214 if(f != null)
215 {
216 return prepRet(f.get(null));
217 }
218 throw new IllegalArgumentException("No matching field found: " + fieldName
219 + " for " + c);
220 }
222 public static Object setStaticField(String className, String fieldName, Object val) throws Exception{
223 Class c = RT.classForName(className);
224 return setStaticField(c, fieldName, val);
225 }
227 public static Object setStaticField(Class c, String fieldName, Object val) throws Exception{
228 Field f = getField(c, fieldName, true);
229 if(f != null)
230 {
231 f.set(null, boxArg(f.getType(), val));
232 return val;
233 }
234 throw new IllegalArgumentException("No matching field found: " + fieldName
235 + " for " + c);
236 }
238 public static Object getInstanceField(Object target, String fieldName) throws Exception{
239 Class c = target.getClass();
240 Field f = getField(c, fieldName, false);
241 if(f != null)
242 {
243 return prepRet(f.get(target));
244 }
245 throw new IllegalArgumentException("No matching field found: " + fieldName
246 + " for " + target.getClass());
247 }
249 public static Object setInstanceField(Object target, String fieldName, Object val) throws Exception{
250 Class c = target.getClass();
251 Field f = getField(c, fieldName, false);
252 if(f != null)
253 {
254 f.set(target, boxArg(f.getType(), val));
255 return val;
256 }
257 throw new IllegalArgumentException("No matching field found: " + fieldName
258 + " for " + target.getClass());
259 }
261 public static Object invokeNoArgInstanceMember(Object target, String name) throws Exception{
262 //favor method over field
263 List meths = getMethods(target.getClass(), 0, name, false);
264 if(meths.size() > 0)
265 return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY);
266 else
267 return getInstanceField(target, name);
268 }
270 public static Object invokeInstanceMember(Object target, String name) throws Exception{
271 //check for field first
272 Class c = target.getClass();
273 Field f = getField(c, name, false);
274 if(f != null) //field get
275 {
276 return prepRet(f.get(target));
277 }
278 return invokeInstanceMethod(target, name, RT.EMPTY_ARRAY);
279 }
281 public static Object invokeInstanceMember(String name, Object target, Object arg1) throws Exception{
282 //check for field first
283 Class c = target.getClass();
284 Field f = getField(c, name, false);
285 if(f != null) //field set
286 {
287 f.set(target, boxArg(f.getType(), arg1));
288 return arg1;
289 }
290 return invokeInstanceMethod(target, name, new Object[]{arg1});
291 }
293 public static Object invokeInstanceMember(String name, Object target, Object... args) throws Exception{
294 return invokeInstanceMethod(target, name, args);
295 }
298 static public Field getField(Class c, String name, boolean getStatics){
299 Field[] allfields = c.getFields();
300 for(int i = 0; i < allfields.length; i++)
301 {
302 if(name.equals(allfields[i].getName())
303 && Modifier.isStatic(allfields[i].getModifiers()) == getStatics)
304 return allfields[i];
305 }
306 return null;
307 }
309 static public List getMethods(Class c, int arity, String name, boolean getStatics){
310 Method[] allmethods = c.getMethods();
311 ArrayList methods = new ArrayList();
312 ArrayList bridgeMethods = new ArrayList();
313 for(int i = 0; i < allmethods.length; i++)
314 {
315 Method method = allmethods[i];
316 if(name.equals(method.getName())
317 && Modifier.isStatic(method.getModifiers()) == getStatics
318 && method.getParameterTypes().length == arity)
319 {
320 try
321 {
322 if(method.isBridge()
323 && c.getMethod(method.getName(), method.getParameterTypes())
324 .equals(method))
325 bridgeMethods.add(method);
326 else
327 methods.add(method);
328 }
329 catch(NoSuchMethodException e)
330 {
331 }
332 }
333 // && (!method.isBridge()
334 // || (c == StringBuilder.class &&
335 // c.getMethod(method.getName(), method.getParameterTypes())
336 // .equals(method))))
337 // {
338 // methods.add(allmethods[i]);
339 // }
340 }
342 if(methods.isEmpty())
343 methods.addAll(bridgeMethods);
345 if(!getStatics && c.isInterface())
346 {
347 allmethods = Object.class.getMethods();
348 for(int i = 0; i < allmethods.length; i++)
349 {
350 if(name.equals(allmethods[i].getName())
351 && Modifier.isStatic(allmethods[i].getModifiers()) == getStatics
352 && allmethods[i].getParameterTypes().length == arity)
353 {
354 methods.add(allmethods[i]);
355 }
356 }
357 }
358 return methods;
359 }
362 static Object boxArg(Class paramType, Object arg){
363 if(!paramType.isPrimitive())
364 return paramType.cast(arg);
365 else if(paramType == boolean.class)
366 return Boolean.class.cast(arg);
367 else if(paramType == char.class)
368 return Character.class.cast(arg);
369 else if(arg instanceof Number)
370 {
371 Number n = (Number) arg;
372 if(paramType == int.class)
373 return n.intValue();
374 else if(paramType == float.class)
375 return n.floatValue();
376 else if(paramType == double.class)
377 return n.doubleValue();
378 else if(paramType == long.class)
379 return n.longValue();
380 else if(paramType == short.class)
381 return n.shortValue();
382 else if(paramType == byte.class)
383 return n.byteValue();
384 }
385 throw new IllegalArgumentException("Unexpected param type, expected: " + paramType +
386 ", given: " + arg.getClass().getName());
387 }
389 static Object[] boxArgs(Class[] params, Object[] args){
390 if(params.length == 0)
391 return null;
392 Object[] ret = new Object[params.length];
393 for(int i = 0; i < params.length; i++)
394 {
395 Object arg = args[i];
396 Class paramType = params[i];
397 ret[i] = boxArg(paramType, arg);
398 }
399 return ret;
400 }
402 static public boolean paramArgTypeMatch(Class paramType, Class argType){
403 if(argType == null)
404 return !paramType.isPrimitive();
405 if(paramType == argType || paramType.isAssignableFrom(argType))
406 return true;
407 if(paramType == int.class)
408 return argType == Integer.class;// || argType == FixNum.class;
409 else if(paramType == float.class)
410 return argType == Float.class;
411 else if(paramType == double.class)
412 return argType == Double.class;// || argType == DoubleNum.class;
413 else if(paramType == long.class)
414 return argType == Long.class;// || argType == BigNum.class;
415 else if(paramType == char.class)
416 return argType == Character.class;
417 else if(paramType == short.class)
418 return argType == Short.class;
419 else if(paramType == byte.class)
420 return argType == Byte.class;
421 else if(paramType == boolean.class)
422 return argType == Boolean.class;
423 return false;
424 }
426 static boolean isCongruent(Class[] params, Object[] args){
427 boolean ret = false;
428 if(args == null)
429 return params.length == 0;
430 if(params.length == args.length)
431 {
432 ret = true;
433 for(int i = 0; ret && i < params.length; i++)
434 {
435 Object arg = args[i];
436 Class argType = (arg == null) ? null : arg.getClass();
437 Class paramType = params[i];
438 ret = paramArgTypeMatch(paramType, argType);
439 }
440 }
441 return ret;
442 }
444 public static Object prepRet(Object x){
445 // if(c == boolean.class)
446 // return ((Boolean) x).booleanValue() ? RT.T : null;
447 if(x instanceof Boolean)
448 return ((Boolean) x)?Boolean.TRUE:Boolean.FALSE;
449 return x;
450 }
451 }