001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.util;
017
018
019import java.lang.reflect.*;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.List;
023import java.util.function.Predicate;
024
025/**
026 * 类实例创建者创建者
027 *
028 * @author michael
029 * @date 17/3/21
030 */
031@SuppressWarnings("unchecked")
032public class ClassUtil {
033
034    private ClassUtil() {
035    }
036
037    private static final String[] OBJECT_METHODS = new String[]{
038        "toString",
039        "getClass",
040        "equals",
041        "hashCode",
042        "wait",
043        "notify",
044        "notifyAll",
045        "clone",
046        "finalize"
047    };
048
049    //proxy frameworks
050    private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
051        // cglib
052        , "org.springframework.cglib.proxy.Factory"
053
054        // javassist
055        , "javassist.util.proxy.ProxyObject"
056        , "org.apache.ibatis.javassist.util.proxy.ProxyObject");
057    private static final String ENHANCER_BY = "$$EnhancerBy";
058    private static final String JAVASSIST_BY = "_$$_";
059
060    public static boolean isProxy(Class<?> clazz) {
061        for (Class<?> cls : clazz.getInterfaces()) {
062            if (PROXY_CLASS_NAMES.contains(cls.getName())) {
063                return true;
064            }
065        }
066        //java proxy
067        return Proxy.isProxyClass(clazz);
068    }
069
070    public static <T> Class<T> getUsefulClass(Class<T> clazz) {
071        if (isProxy(clazz)) {
072            return getJdkProxySuperClass(clazz);
073        }
074
075        //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello   -------> Guice
076        //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158  ----> CGLIB
077        //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist
078
079        final String name = clazz.getName();
080        if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
081            return (Class<T>) clazz.getSuperclass();
082        }
083
084        return clazz;
085    }
086
087
088    public static Class<?> getWrapType(Class<?> clazz) {
089        if (clazz == null || !clazz.isPrimitive()) {
090            return clazz;
091        }
092        if (clazz == Integer.TYPE) {
093            return Integer.class;
094        } else if (clazz == Long.TYPE) {
095            return Long.class;
096        } else if (clazz == Boolean.TYPE) {
097            return Boolean.class;
098        } else if (clazz == Float.TYPE) {
099            return Float.class;
100        } else if (clazz == Double.TYPE) {
101            return Double.class;
102        } else if (clazz == Short.TYPE) {
103            return Short.class;
104        } else if (clazz == Character.TYPE) {
105            return Character.class;
106        } else if (clazz == Byte.TYPE) {
107            return Byte.class;
108        } else if (clazz == Void.TYPE) {
109            return Void.class;
110        }
111        return clazz;
112    }
113
114
115    public static boolean isArray(Class<?> clazz) {
116        return clazz.isArray()
117            || clazz == int[].class
118            || clazz == long[].class
119            || clazz == short[].class
120            || clazz == float[].class
121            || clazz == double[].class;
122    }
123
124    public static boolean canInstance(int mod) {
125        return !Modifier.isAbstract(mod) || !Modifier.isInterface(mod);
126    }
127
128
129    public static <T> T newInstance(Class<T> clazz) {
130        try {
131            Constructor<?> defaultConstructor = null;
132            Constructor<?> otherConstructor = null;
133
134            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
135            for (Constructor<?> constructor : declaredConstructors) {
136                if (constructor.getParameterCount() == 0 && Modifier.isPublic(constructor.getModifiers())) {
137                    defaultConstructor = constructor;
138                } else if (Modifier.isPublic(constructor.getModifiers())) {
139                    otherConstructor = constructor;
140                }
141            }
142            if (defaultConstructor != null) {
143                return (T) defaultConstructor.newInstance();
144            } else if (otherConstructor != null) {
145                Class<?>[] parameterTypes = otherConstructor.getParameterTypes();
146                Object[] parameters = new Object[parameterTypes.length];
147                for (int i = 0; i < parameterTypes.length; i++) {
148                    if (parameterTypes[i].isPrimitive()) {
149                        parameters[i] = ConvertUtil.getPrimitiveDefaultValue(parameterTypes[i]);
150                    } else {
151                        parameters[i] = null;
152                    }
153                }
154                return (T) otherConstructor.newInstance(parameters);
155            }
156            // 没有任何构造函数的情况下,去查找 static 工厂方法,满足 lombok 注解的需求
157            else {
158                Method factoryMethod = ClassUtil.getFirstMethod(clazz, m -> m.getParameterCount() == 0
159                    && clazz == m.getReturnType()
160                    && Modifier.isPublic(m.getModifiers())
161                    && Modifier.isStatic(m.getModifiers()));
162                if (factoryMethod != null) {
163                    return (T) factoryMethod.invoke(null);
164                }
165            }
166            throw new IllegalArgumentException("the class \"" + clazz.getName() + "\" has no constructor.");
167        } catch (Exception e) {
168            throw new RuntimeException("Can not newInstance class: " + clazz.getName());
169        }
170    }
171
172
173    public static <T> T newInstance(Class<T> clazz, Object... paras) {
174        try {
175            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
176            for (Constructor<?> constructor : constructors) {
177                if (isMatchedParas(constructor, paras)) {
178                    Object ret = constructor.newInstance(paras);
179                    return (T) ret;
180                }
181            }
182            throw new IllegalArgumentException("Can not find constructor by paras: \"" + Arrays.toString(paras) + "\" in class[" + clazz.getName() + "]");
183        } catch (Exception e) {
184            e.printStackTrace();
185        }
186
187        return null;
188    }
189
190
191    private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) {
192        if (constructor.getParameterCount() == 0) {
193            return paras == null || paras.length == 0;
194        }
195
196        if (constructor.getParameterCount() > 0
197            && (paras == null || paras.length != constructor.getParameterCount())) {
198            return false;
199        }
200
201        Class<?>[] parameterTypes = constructor.getParameterTypes();
202        for (int i = 0; i < parameterTypes.length; i++) {
203            Class<?> parameterType = parameterTypes[i];
204            Object paraObject = paras[i];
205            if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
206                return false;
207            }
208        }
209
210        return true;
211    }
212
213
214    public static List<Field> getAllFields(Class<?> clazz) {
215        List<Field> fields = new ArrayList<>();
216        doGetFields(clazz, fields, null, false);
217        return fields;
218    }
219
220    public static List<Field> getAllFields(Class<?> clazz, Predicate<Field> predicate) {
221        List<Field> fields = new ArrayList<>();
222        doGetFields(clazz, fields, predicate, false);
223        return fields;
224    }
225
226    public static Field getFirstField(Class<?> clazz, Predicate<Field> predicate) {
227        List<Field> fields = new ArrayList<>();
228        doGetFields(clazz, fields, predicate, true);
229        return fields.isEmpty() ? null : fields.get(0);
230    }
231
232    private static void doGetFields(Class<?> clazz, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
233        if (clazz == null || clazz == Object.class) {
234            return;
235        }
236
237        Field[] declaredFields = clazz.getDeclaredFields();
238        for (Field declaredField : declaredFields) {
239            if (predicate == null || predicate.test(declaredField)) {
240                fields.add(declaredField);
241                if (firstOnly) {
242                    break;
243                }
244            }
245        }
246
247        if (firstOnly && !fields.isEmpty()) {
248            return;
249        }
250
251        doGetFields(clazz.getSuperclass(), fields, predicate, firstOnly);
252    }
253
254    public static List<Method> getAllMethods(Class<?> clazz) {
255        List<Method> methods = new ArrayList<>();
256        doGetMethods(clazz, methods, null, false);
257        return methods;
258    }
259
260    public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method> predicate) {
261        List<Method> methods = new ArrayList<>();
262        doGetMethods(clazz, methods, predicate, false);
263        return methods;
264    }
265
266    public static Method getAnyMethod(Class<?> clazz, String... methodNames) {
267        return getFirstMethod(clazz, method -> ArrayUtil.contains(methodNames, method.getName()));
268    }
269
270    public static Method getFirstMethod(Class<?> clazz, Predicate<Method> predicate) {
271        List<Method> methods = new ArrayList<>();
272        doGetMethods(clazz, methods, predicate, true);
273        return methods.isEmpty() ? null : methods.get(0);
274    }
275
276
277    private static void doGetMethods(Class<?> clazz, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
278        if (clazz == null || clazz == Object.class) {
279            return;
280        }
281
282        Method[] declaredMethods = clazz.getDeclaredMethods();
283        for (Method method : declaredMethods) {
284            if (predicate == null || predicate.test(method)) {
285                methods.add(method);
286                if (firstOnly) {
287                    break;
288                }
289            }
290        }
291
292        if (firstOnly && !methods.isEmpty()) {
293            return;
294        }
295
296        doGetMethods(clazz.getSuperclass(), methods, predicate, firstOnly);
297    }
298
299
300    private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
301        final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());
302        return (Class<T>) proxyClass.getInterfaces()[0];
303    }
304
305
306    public static boolean isGetterMethod(Method method, String property) {
307        String methodName = method.getName();
308        if (methodName.startsWith("get") && methodName.length() > 3) {
309            return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(3));
310        } else if (methodName.startsWith("is") && methodName.length() > 2) {
311            return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(2));
312        } else {
313            return false;
314        }
315    }
316
317    public static boolean isObjectMethod(String methodName) {
318        return ArrayUtil.contains(OBJECT_METHODS, methodName);
319    }
320
321}