ReflectUtils.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
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 */
16package org.mockito.cglib.core;
17
18import java.beans.*;
19import java.lang.reflect.*;
20import java.security.AccessController;
21import java.security.PrivilegedAction;
22import java.security.ProtectionDomain;
23import java.util.*;
24
25import org.mockito.asm.Attribute;
26import org.mockito.asm.Type;
27
28/**
29 * @version $Id: ReflectUtils.java,v 1.29 2006/02/28 00:30:51 herbyderby Exp $
30 */
31public 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