1 /* 2 * Copyright 2003,2004 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.mockito.cglib.core; 17 18 import java.beans.*; 19 import java.lang.reflect.*; 20 import java.security.AccessController; 21 import java.security.PrivilegedAction; 22 import java.security.ProtectionDomain; 23 import java.util.*; 24 25 import org.mockito.asm.Attribute; 26 import org.mockito.asm.Type; 27 28 /** 29 * @version $Id: ReflectUtils.java,v 1.29 2006/02/28 00:30:51 herbyderby Exp $ 30 */ 31 public class ReflectUtils { 32 private ReflectUtils() { } 33 34 private static final Map primitives = new HashMap(8); 35 private static final Map transforms = new HashMap(8); 36 private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader(); 37 private static Method DEFINE_CLASS; 38 private static final ProtectionDomain PROTECTION_DOMAIN; 39 40 static { 41 PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() { 42 public Object run() { 43 return ReflectUtils.class.getProtectionDomain(); 44 } 45 }); 46 47 AccessController.doPrivileged(new PrivilegedAction() { 48 public Object run() { 49 try { 50 Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this 51 DEFINE_CLASS = loader.getDeclaredMethod("defineClass", 52 new Class[]{ String.class, 53 byte[].class, 54 Integer.TYPE, 55 Integer.TYPE, 56 ProtectionDomain.class }); 57 DEFINE_CLASS.setAccessible(true); 58 } catch (ClassNotFoundException e) { 59 throw new CodeGenerationException(e); 60 } catch (NoSuchMethodException e) { 61 throw new CodeGenerationException(e); 62 } 63 return null; 64 } 65 }); 66 } 67 68 private static final String[] CGLIB_PACKAGES = { 69 "java.lang", 70 }; 71 72 static { 73 primitives.put("byte", Byte.TYPE); 74 primitives.put("char", Character.TYPE); 75 primitives.put("double", Double.TYPE); 76 primitives.put("float", Float.TYPE); 77 primitives.put("int", Integer.TYPE); 78 primitives.put("long", Long.TYPE); 79 primitives.put("short", Short.TYPE); 80 primitives.put("boolean", Boolean.TYPE); 81 82 transforms.put("byte", "B"); 83 transforms.put("char", "C"); 84 transforms.put("double", "D"); 85 transforms.put("float", "F"); 86 transforms.put("int", "I"); 87 transforms.put("long", "J"); 88 transforms.put("short", "S"); 89 transforms.put("boolean", "Z"); 90 } 91 92 public static Type[] getExceptionTypes(Member member) { 93 if (member instanceof Method) { 94 return TypeUtils.getTypes(((Method)member).getExceptionTypes()); 95 } else if (member instanceof Constructor) { 96 return TypeUtils.getTypes(((Constructor)member).getExceptionTypes()); 97 } else { 98 throw new IllegalArgumentException("Cannot get exception types of a field"); 99 } 100 } 101 102 public static Signature getSignature(Member member) { 103 if (member instanceof Method) { 104 return new Signature(member.getName(), Type.getMethodDescriptor((Method)member)); 105 } else if (member instanceof Constructor) { 106 Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes()); 107 return new Signature(Constants.CONSTRUCTOR_NAME, 108 Type.getMethodDescriptor(Type.VOID_TYPE, types)); 109 110 } else { 111 throw new IllegalArgumentException("Cannot get signature of a field"); 112 } 113 } 114 115 public static Constructor findConstructor(String desc) { 116 return findConstructor(desc, defaultLoader); 117 } 118 119 public static Constructor findConstructor(String desc, ClassLoader loader) { 120 try { 121 int lparen = desc.indexOf('('); 122 String className = desc.substring(0, lparen).trim(); 123 return getClass(className, loader).getConstructor(parseTypes(desc, loader)); 124 } catch (ClassNotFoundException e) { 125 throw new CodeGenerationException(e); 126 } catch (NoSuchMethodException e) { 127 throw new CodeGenerationException(e); 128 } 129 } 130 131 public static Method findMethod(String desc) { 132 return findMethod(desc, defaultLoader); 133 } 134 135 public static Method findMethod(String desc, ClassLoader loader) { 136 try { 137 int lparen = desc.indexOf('('); 138 int dot = desc.lastIndexOf('.', lparen); 139 String className = desc.substring(0, dot).trim(); 140 String methodName = desc.substring(dot + 1, lparen).trim(); 141 return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader)); 142 } catch (ClassNotFoundException e) { 143 throw new CodeGenerationException(e); 144 } catch (NoSuchMethodException e) { 145 throw new CodeGenerationException(e); 146 } 147 } 148 149 private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException { 150 int lparen = desc.indexOf('('); 151 int rparen = desc.indexOf(')', lparen); 152 List params = new ArrayList(); 153 int start = lparen + 1; 154 for (;;) { 155 int comma = desc.indexOf(',', start); 156 if (comma < 0) { 157 break; 158 } 159 params.add(desc.substring(start, comma).trim()); 160 start = comma + 1; 161 } 162 if (start < rparen) { 163 params.add(desc.substring(start, rparen).trim()); 164 } 165 Class[] types = new Class[params.size()]; 166 for (int i = 0; i < types.length; i++) { 167 types[i] = getClass((String)params.get(i), loader); 168 } 169 return types; 170 } 171 172 private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException { 173 return getClass(className, loader, CGLIB_PACKAGES); 174 } 175 176 private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException { 177 String save = className; 178 int dimensions = 0; 179 int index = 0; 180 while ((index = className.indexOf("[]", index) + 1) > 0) { 181 dimensions++; 182 } 183 StringBuffer brackets = new StringBuffer(className.length() - dimensions); 184 for (int i = 0; i < dimensions; i++) { 185 brackets.append('['); 186 } 187 className = className.substring(0, className.length() - 2 * dimensions); 188 189 String prefix = (dimensions > 0) ? brackets + "L" : ""; 190 String suffix = (dimensions > 0) ? ";" : ""; 191 try { 192 return Class.forName(prefix + className + suffix, false, loader); 193 } catch (ClassNotFoundException ignore) { } 194 for (int i = 0; i < packages.length; i++) { 195 try { 196 return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader); 197 } catch (ClassNotFoundException ignore) { } 198 } 199 if (dimensions == 0) { 200 Class c = (Class)primitives.get(className); 201 if (c != null) { 202 return c; 203 } 204 } else { 205 String transform = (String)transforms.get(className); 206 if (transform != null) { 207 try { 208 return Class.forName(brackets + transform, false, loader); 209 } catch (ClassNotFoundException ignore) { } 210 } 211 } 212 throw new ClassNotFoundException(save); 213 } 214 215 216 public static Object newInstance(Class type) { 217 return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null); 218 } 219 220 public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) { 221 return newInstance(getConstructor(type, parameterTypes), args); 222 } 223 224 public static Object newInstance(final Constructor cstruct, final Object[] args) { 225 226 boolean flag = cstruct.isAccessible(); 227 try { 228 cstruct.setAccessible(true); 229 Object result = cstruct.newInstance(args); 230 return result; 231 } catch (InstantiationException e) { 232 throw new CodeGenerationException(e); 233 } catch (IllegalAccessException e) { 234 throw new CodeGenerationException(e); 235 } catch (InvocationTargetException e) { 236 throw new CodeGenerationException(e.getTargetException()); 237 } finally { 238 cstruct.setAccessible(flag); 239 } 240 241 } 242 243 public static Constructor getConstructor(Class type, Class[] parameterTypes) { 244 try { 245 Constructor constructor = type.getDeclaredConstructor(parameterTypes); 246 constructor.setAccessible(true); 247 return constructor; 248 } catch (NoSuchMethodException e) { 249 throw new CodeGenerationException(e); 250 } 251 } 252 253 public static String[] getNames(Class[] classes) 254 { 255 if (classes == null) 256 return null; 257 String[] names = new String[classes.length]; 258 for (int i = 0; i < names.length; i++) { 259 names[i] = classes[i].getName(); 260 } 261 return names; 262 } 263 264 public static Class[] getClasses(Object[] objects) { 265 Class[] classes = new Class[objects.length]; 266 for (int i = 0; i < objects.length; i++) { 267 classes[i] = objects[i].getClass(); 268 } 269 return classes; 270 } 271 272 public static Method findNewInstance(Class iface) { 273 Method m = findInterfaceMethod(iface); 274 if (!m.getName().equals("newInstance")) { 275 throw new IllegalArgumentException(iface + " missing newInstance method"); 276 } 277 return m; 278 } 279 280 public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) { 281 Set methods = new HashSet(); 282 for (int i = 0; i < properties.length; i++) { 283 PropertyDescriptor pd = properties[i]; 284 if (read) { 285 methods.add(pd.getReadMethod()); 286 } 287 if (write) { 288 methods.add(pd.getWriteMethod()); 289 } 290 } 291 methods.remove(null); 292 return (Method[])methods.toArray(new Method[methods.size()]); 293 } 294 295 public static PropertyDescriptor[] getBeanProperties(Class type) { 296 return getPropertiesHelper(type, true, true); 297 } 298 299 public static PropertyDescriptor[] getBeanGetters(Class type) { 300 return getPropertiesHelper(type, true, false); 301 } 302 303 public static PropertyDescriptor[] getBeanSetters(Class type) { 304 return getPropertiesHelper(type, false, true); 305 } 306 307 private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { 308 try { 309 BeanInfo info = Introspector.getBeanInfo(type, Object.class); 310 PropertyDescriptor[] all = info.getPropertyDescriptors(); 311 if (read && write) { 312 return all; 313 } 314 List properties = new ArrayList(all.length); 315 for (int i = 0; i < all.length; i++) { 316 PropertyDescriptor pd = all[i]; 317 if ((read && pd.getReadMethod() != null) || 318 (write && pd.getWriteMethod() != null)) { 319 properties.add(pd); 320 } 321 } 322 return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]); 323 } catch (IntrospectionException e) { 324 throw new CodeGenerationException(e); 325 } 326 } 327 328 329 330 public static Method findDeclaredMethod(final Class type, 331 final String methodName, final Class[] parameterTypes) 332 throws NoSuchMethodException { 333 334 Class cl = type; 335 while (cl != null) { 336 try { 337 return cl.getDeclaredMethod(methodName, parameterTypes); 338 } catch (NoSuchMethodException e) { 339 cl = cl.getSuperclass(); 340 } 341 } 342 throw new NoSuchMethodException(methodName); 343 344 } 345 346 public static List addAllMethods(final Class type, final List list) { 347 348 349 list.addAll(java.util.Arrays.asList(type.getDeclaredMethods())); 350 Class superclass = type.getSuperclass(); 351 if (superclass != null) { 352 addAllMethods(superclass, list); 353 } 354 Class[] interfaces = type.getInterfaces(); 355 for (int i = 0; i < interfaces.length; i++) { 356 addAllMethods(interfaces[i], list); 357 } 358 359 return list; 360 } 361 362 public static List addAllInterfaces(Class type, List list) { 363 Class superclass = type.getSuperclass(); 364 if (superclass != null) { 365 list.addAll(Arrays.asList(type.getInterfaces())); 366 addAllInterfaces(superclass, list); 367 } 368 return list; 369 } 370 371 372 public static Method findInterfaceMethod(Class iface) { 373 if (!iface.isInterface()) { 374 throw new IllegalArgumentException(iface + " is not an interface"); 375 } 376 Method[] methods = iface.getDeclaredMethods(); 377 if (methods.length != 1) { 378 throw new IllegalArgumentException("expecting exactly 1 method in " + iface); 379 } 380 return methods[0]; 381 } 382 383 public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { 384 Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN }; 385 return (Class)DEFINE_CLASS.invoke(loader, args); 386 } 387 388 public static int findPackageProtected(Class[] classes) { 389 for (int i = 0; i < classes.length; i++) { 390 if (!Modifier.isPublic(classes[i].getModifiers())) { 391 return i; 392 } 393 } 394 return 0; 395 } 396 397 public static MethodInfo getMethodInfo(final Member member, final int modifiers) { 398 final Signature sig = getSignature(member); 399 return new MethodInfo() { 400 private ClassInfo ci; 401 public ClassInfo getClassInfo() { 402 if (ci == null) 403 ci = ReflectUtils.getClassInfo(member.getDeclaringClass()); 404 return ci; 405 } 406 public int getModifiers() { 407 return modifiers; 408 } 409 public Signature getSignature() { 410 return sig; 411 } 412 public Type[] getExceptionTypes() { 413 return ReflectUtils.getExceptionTypes(member); 414 } 415 public Attribute getAttribute() { 416 return null; 417 } 418 }; 419 } 420 421 public static MethodInfo getMethodInfo(Member member) { 422 return getMethodInfo(member, member.getModifiers()); 423 } 424 425 public static ClassInfo getClassInfo(final Class clazz) { 426 final Type type = Type.getType(clazz); 427 final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass()); 428 return new ClassInfo() { 429 public Type getType() { 430 return type; 431 } 432 public Type getSuperType() { 433 return sc; 434 } 435 public Type[] getInterfaces() { 436 return TypeUtils.getTypes(clazz.getInterfaces()); 437 } 438 public int getModifiers() { 439 return clazz.getModifiers(); 440 } 441 }; 442 } 443 444 // used by MethodInterceptorGenerated generated code 445 public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) 446 { 447 Map map = new HashMap(); 448 for (int i = 0; i < methods.length; i++) { 449 Method method = methods[i]; 450 map.put(method.getName() + Type.getMethodDescriptor(method), method); 451 } 452 Method[] result = new Method[namesAndDescriptors.length / 2]; 453 for (int i = 0; i < result.length; i++) { 454 result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]); 455 if (result[i] == null) { 456 // TODO: error? 457 } 458 } 459 return result; 460 } 461 } 462