1/*
2 * Copyright (C) 2015 The Android Open Source Project
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
17package com.android.layoutlib.bridge.util;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21
22import java.lang.reflect.InvocationHandler;
23import java.lang.reflect.InvocationTargetException;
24import java.lang.reflect.Method;
25import java.lang.reflect.Proxy;
26
27/**
28 * Utility to convert checked Reflection exceptions to unchecked exceptions.
29 */
30public class ReflectionUtils {
31
32    @NonNull
33    public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
34            @Nullable Class<?>... params) throws ReflectionException {
35        try {
36            return clazz.getMethod(name, params);
37        } catch (NoSuchMethodException e) {
38            throw new ReflectionException(e);
39        }
40    }
41
42    @NonNull
43    public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
44      @Nullable Class<?>... params) throws ReflectionException {
45        Method method = getMethod(clazz, name, params);
46        method.setAccessible(true);
47
48        return method;
49    }
50
51    @Nullable
52    public static Object invoke(@NonNull Method method, @Nullable Object object,
53            @Nullable Object... args) throws ReflectionException {
54        Exception ex;
55        try {
56            return method.invoke(object, args);
57        } catch (IllegalAccessException | InvocationTargetException e) {
58            ex = e;
59        }
60        throw new ReflectionException(ex);
61    }
62
63    /**
64     * Check if the object is an instance of a class named {@code className}. This doesn't work
65     * for interfaces.
66     */
67    public static boolean isInstanceOf(Object object, String className) {
68        Class superClass = object.getClass();
69        while (superClass != null) {
70            String name = superClass.getName();
71            if (name.equals(className)) {
72                return true;
73            }
74            superClass = superClass.getSuperclass();
75        }
76        return false;
77    }
78
79    /**
80     * Check if the object is an instance of any of the class named in {@code className}. This
81     * doesn't work for interfaces.
82     */
83    public static boolean isInstanceOf(Object object, String[] classNames) {
84        Class superClass = object.getClass();
85        while (superClass != null) {
86            String name = superClass.getName();
87            for (String className : classNames) {
88                if (name.equals(className)) {
89                    return true;
90                }
91            }
92            superClass = superClass.getSuperclass();
93        }
94        return false;
95    }
96
97    @NonNull
98    public static Throwable getCause(@NonNull Throwable throwable) {
99        Throwable cause = throwable.getCause();
100        return cause == null ? throwable : cause;
101    }
102
103    /**
104     * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
105     * the name {@code className}.
106     * <p>
107     * This is used when we cannot use Class.forName() since the class we want was loaded from a
108     * different ClassLoader.
109     */
110    @NonNull
111    public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
112        Class<?> superClass = object.getClass();
113        while (superClass != null) {
114            if (className.equals(superClass.getName())) {
115                return superClass;
116            }
117            superClass = superClass.getSuperclass();
118        }
119        throw new RuntimeException("invalid object/classname combination.");
120    }
121
122    public static <T> T createProxy(Class<T> interfaze) {
123        ClassLoader loader = interfaze.getClassLoader();
124        return (T) Proxy.newProxyInstance(loader, new Class[]{interfaze}, new InvocationHandler() {
125            public Object invoke(Object proxy, Method m, Object[] args) {
126                final Class<?> returnType = m.getReturnType();
127                if (returnType == boolean.class) {
128                    return false;
129                } else if (returnType == int.class) {
130                    return 0;
131                } else if (returnType == long.class) {
132                    return 0L;
133                } else if (returnType == short.class) {
134                    return 0;
135                } else if (returnType == char.class) {
136                    return 0;
137                } else if (returnType == byte.class) {
138                    return 0;
139                } else if (returnType == float.class) {
140                    return 0f;
141                } else if (returnType == double.class) {
142                    return 0.0;
143                } else {
144                    return null;
145                }
146            }
147        });
148    }
149
150    /**
151     * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
152     * introduced in 1.7 and we are still on 1.6
153     */
154    public static class ReflectionException extends Exception {
155        public ReflectionException() {
156            super();
157        }
158
159        public ReflectionException(String message) {
160            super(message);
161        }
162
163        public ReflectionException(String message, Throwable cause) {
164            super(message, cause);
165        }
166
167        public ReflectionException(Throwable cause) {
168            super(cause);
169        }
170    }
171}
172