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 org.apache.ibatis.javassist.util.proxy.ProxyObject;
020
021import java.lang.reflect.*;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.List;
025import java.util.function.Predicate;
026
027/**
028 * 类实例创建者创建者
029 *
030 * @author michael
031 * @date 17/3/21
032 */
033@SuppressWarnings("unchecked")
034public class ClassUtil {
035
036    private ClassUtil() {
037    }
038
039    private static final String[] OBJECT_METHODS = new String[]{
040        "toString",
041        "getClass",
042        "equals",
043        "hashCode",
044        "wait",
045        "notify",
046        "notifyAll",
047        "clone",
048        "finalize"
049    };
050
051    //proxy frameworks
052    private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
053        // cglib
054        , "org.springframework.cglib.proxy.Factory"
055
056        // javassist
057        , "javassist.util.proxy.ProxyObject"
058        , "org.apache.ibatis.javassist.util.proxy.ProxyObject");
059    private static final String ENHANCER_BY = "$$EnhancerBy";
060    private static final String JAVASSIST_BY = "_$$_";
061
062    public static boolean isProxy(Class<?> clazz) {
063        for (Class<?> cls : clazz.getInterfaces()) {
064            if (PROXY_CLASS_NAMES.contains(cls.getName())) {
065                return true;
066            }
067        }
068        //java proxy
069        return Proxy.isProxyClass(clazz);
070    }
071
072    public static <T> Class<T> getUsefulClass(Class<T> clazz) {
073
074        if (ProxyObject.class.isAssignableFrom(clazz)) {
075            return (Class<T>) clazz.getSuperclass();
076        }
077
078        if (isProxy(clazz)) {
079            return getJdkProxySuperClass(clazz);
080        }
081
082        //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello   -------> Guice
083        //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158  ----> CGLIB
084        //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist
085        final String name = clazz.getName();
086        if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
087            return (Class<T>) clazz.getSuperclass();
088        }
089
090        return clazz;
091    }
092
093
094    public static Class<?> getWrapType(Class<?> clazz) {
095        if (clazz == null || !clazz.isPrimitive()) {
096            return clazz;
097        }
098        if (clazz == Integer.TYPE) {
099            return Integer.class;
100        } else if (clazz == Long.TYPE) {
101            return Long.class;
102        } else if (clazz == Boolean.TYPE) {
103            return Boolean.class;
104        } else if (clazz == Float.TYPE) {
105            return Float.class;
106        } else if (clazz == Double.TYPE) {
107            return Double.class;
108        } else if (clazz == Short.TYPE) {
109            return Short.class;
110        } else if (clazz == Character.TYPE) {
111            return Character.class;
112        } else if (clazz == Byte.TYPE) {
113            return Byte.class;
114        } else if (clazz == Void.TYPE) {
115            return Void.class;
116        }
117        return clazz;
118    }
119
120
121    public static boolean isArray(Class<?> clazz) {
122        return clazz.isArray()
123            || clazz == int[].class
124            || clazz == long[].class
125            || clazz == short[].class
126            || clazz == float[].class
127            || clazz == double[].class;
128    }
129
130    public static boolean canInstance(int mod) {
131        return !Modifier.isAbstract(mod) || !Modifier.isInterface(mod);
132    }
133
134
135    public static <T> T newInstance(Class<T> clazz) {
136        try {
137            Constructor<?> defaultConstructor = null;
138            Constructor<?> otherConstructor = null;
139
140            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
141            for (Constructor<?> constructor : declaredConstructors) {
142                if (constructor.getParameterCount() == 0 && Modifier.isPublic(constructor.getModifiers())) {
143                    defaultConstructor = constructor;
144                } else if (Modifier.isPublic(constructor.getModifiers())) {
145                    otherConstructor = constructor;
146                }
147            }
148            if (defaultConstructor != null) {
149                return (T) defaultConstructor.newInstance();
150            } else if (otherConstructor != null) {
151                Class<?>[] parameterTypes = otherConstructor.getParameterTypes();
152                Object[] parameters = new Object[parameterTypes.length];
153                for (int i = 0; i < parameterTypes.length; i++) {
154                    if (parameterTypes[i].isPrimitive()) {
155                        parameters[i] = ConvertUtil.getPrimitiveDefaultValue(parameterTypes[i]);
156                    } else {
157                        parameters[i] = null;
158                    }
159                }
160                return (T) otherConstructor.newInstance(parameters);
161            }
162            // 没有任何构造函数的情况下,去查找 static 工厂方法,满足 lombok 注解的需求
163            else {
164                Method factoryMethod = ClassUtil.getFirstMethod(clazz, m -> m.getParameterCount() == 0
165                    && m.getReturnType().isAssignableFrom(clazz)
166                    && Modifier.isPublic(m.getModifiers())
167                    && Modifier.isStatic(m.getModifiers()));
168
169                if (factoryMethod != null) {
170                    return (T) factoryMethod.invoke(null);
171                }
172            }
173            throw new IllegalArgumentException("the class \"" + clazz.getName() + "\" has no constructor.");
174        } catch (Exception e) {
175            throw new RuntimeException("Can not newInstance class: " + clazz.getName(), e);
176        }
177    }
178
179
180    public static <T> T newInstance(Class<T> clazz, Object... paras) {
181        try {
182            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
183            for (Constructor<?> constructor : constructors) {
184                if (isMatchedParas(constructor, paras)) {
185                    Object ret = constructor.newInstance(paras);
186                    return (T) ret;
187                }
188            }
189            throw new IllegalArgumentException("Can not find constructor by paras: \"" + Arrays.toString(paras) + "\" in class[" + clazz.getName() + "]");
190        } catch (Exception e) {
191            throw new RuntimeException(e.toString(), e);
192        }
193    }
194
195
196    private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) {
197        if (constructor.getParameterCount() == 0) {
198            return paras == null || paras.length == 0;
199        }
200
201        if (constructor.getParameterCount() > 0
202            && (paras == null || paras.length != constructor.getParameterCount())) {
203            return false;
204        }
205
206        Class<?>[] parameterTypes = constructor.getParameterTypes();
207        for (int i = 0; i < parameterTypes.length; i++) {
208            Class<?> parameterType = parameterTypes[i];
209            Object paraObject = paras[i];
210            if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
211                return false;
212            }
213        }
214
215        return true;
216    }
217
218
219    public static List<Field> getAllFields(Class<?> clazz) {
220        List<Field> fields = new ArrayList<>();
221        doGetFields(clazz, fields, null, false);
222        return fields;
223    }
224
225    public static List<Field> getAllFields(Class<?> clazz, Predicate<Field> predicate) {
226        List<Field> fields = new ArrayList<>();
227        doGetFields(clazz, fields, predicate, false);
228        return fields;
229    }
230
231    public static Field getFirstField(Class<?> clazz, Predicate<Field> predicate) {
232        List<Field> fields = new ArrayList<>();
233        doGetFields(clazz, fields, predicate, true);
234        return fields.isEmpty() ? null : fields.get(0);
235    }
236
237    /**
238     * 应用类及其除Object外的所有父类
239     *
240     * @param clazz  需要应用的类
241     * @param checkToContinue 应用当前类并检测是否继续应用, 返回false则停止应用, 返回true继续向上取父类
242     * @author KAMOsama
243     */
244    public static void applyAllClass(Class<?> clazz,  Predicate<Class<?>> checkToContinue) {
245        Class<?> currentClass = clazz;
246        while (currentClass != null && currentClass != Object.class && checkToContinue.test(currentClass)) {
247            currentClass = currentClass.getSuperclass();
248        }
249    }
250
251    private static void doGetFields(Class<?> clazz, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
252        applyAllClass(clazz, currentClass -> {
253            Field[] declaredFields = currentClass.getDeclaredFields();
254            for (Field declaredField : declaredFields) {
255                if (predicate == null || predicate.test(declaredField)) {
256                    fields.add(declaredField);
257                    if (firstOnly) {
258                        break;
259                    }
260                }
261            }
262            // 不止要获取第一个或集合为空就继续获取遍历父类
263            return !firstOnly || fields.isEmpty();
264        });
265    }
266
267    public static List<Method> getAllMethods(Class<?> clazz) {
268        List<Method> methods = new ArrayList<>();
269        doGetMethods(clazz, methods, null, false);
270        return methods;
271    }
272
273    public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method> predicate) {
274        List<Method> methods = new ArrayList<>();
275        doGetMethods(clazz, methods, predicate, false);
276        return methods;
277    }
278
279    public static Method getAnyMethod(Class<?> clazz, String... methodNames) {
280        return getFirstMethod(clazz, method -> ArrayUtil.contains(methodNames, method.getName()));
281    }
282
283    public static Method getFirstMethod(Class<?> clazz, Predicate<Method> predicate) {
284        List<Method> methods = new ArrayList<>();
285        doGetMethods(clazz, methods, predicate, true);
286        return methods.isEmpty() ? null : methods.get(0);
287    }
288
289
290    private static void doGetMethods(Class<?> clazz, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
291        applyAllClass(clazz, currentClass -> {
292            Method[] declaredMethods = currentClass.getDeclaredMethods();
293            if (currentClass.isInterface()) {
294                for (Method method : declaredMethods) {
295                    // 接口类只需要获取 default 方法
296                    if (method.isDefault() && (predicate == null || predicate.test(method))) {
297                        methods.add(method);
298                        if (firstOnly) {
299                            break;
300                        }
301                    }
302                }
303            } else {
304                for (Method method : declaredMethods) {
305                    if (predicate == null || predicate.test(method)) {
306                        methods.add(method);
307                        if (firstOnly) {
308                            break;
309                        }
310                    }
311                }
312            }
313            // 只获取第一个并且集合不为空就结束遍历
314            if (firstOnly && !methods.isEmpty()) {
315                return false;
316            }
317            Class<?>[] interfaces = currentClass.getInterfaces();
318            for (Class<?> anInterface : interfaces) {
319                doGetMethods(anInterface, methods, predicate, firstOnly);
320                // 只获取第一个并且集合不为空就结束遍历
321                if (firstOnly && !methods.isEmpty()){
322                    return false;
323                }
324            }
325            return true;
326        });
327    }
328
329    private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
330        final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());
331        return (Class<T>) proxyClass.getInterfaces()[0];
332    }
333
334
335    public static boolean isGetterMethod(Method method, String property) {
336        String methodName = method.getName();
337        if (methodName.startsWith("get") && methodName.length() > 3) {
338            return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(3));
339        } else if (methodName.startsWith("is") && methodName.length() > 2) {
340            return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(2));
341        } else {
342            return false;
343        }
344    }
345
346    public static boolean isObjectMethod(String methodName) {
347        return ArrayUtil.contains(OBJECT_METHODS, methodName);
348    }
349
350}