Home | History | Annotate | Download | only in core
      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