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