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 * Created by michael on 17/3/21.
028 */
029public class ClassUtil {
030
031    private ClassUtil() {}
032
033    //proxy frameworks
034    private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
035            // cglib
036            , "org.springframework.cglib.proxy.Factory"
037
038            // javassist
039            , "javassist.util.proxy.ProxyObject"
040            , "org.apache.ibatis.javassist.util.proxy.ProxyObject");
041    private static final String ENHANCER_BY = "$$EnhancerBy";
042    private static final String JAVASSIST_BY = "_$$_";
043
044    public static boolean isProxy(Class<?> clazz) {
045        for (Class<?> cls : clazz.getInterfaces()) {
046            if (PROXY_CLASS_NAMES.contains(cls.getName())) {
047                return true;
048            }
049        }
050        //java proxy
051        return Proxy.isProxyClass(clazz);
052    }
053
054    public static <T> Class<T> getUsefulClass(Class<T> clazz) {
055        if (isProxy(clazz)) {
056            return getJdkProxySuperClass(clazz);
057        }
058
059        //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello   -------> Guice
060        //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158  ----> CGLIB
061        //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist
062
063        final String name = clazz.getName();
064        if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
065            return (Class<T>) clazz.getSuperclass();
066        }
067
068        return clazz;
069    }
070
071
072    public static Class<?> getWrapType(Class<?> clazz) {
073        if (clazz == null || !clazz.isPrimitive()) {
074            return clazz;
075        }
076        if (clazz == Integer.TYPE) {
077            return Integer.class;
078        } else if (clazz == Long.TYPE) {
079            return Long.class;
080        } else if (clazz == Boolean.TYPE) {
081            return Boolean.class;
082        } else if (clazz == Float.TYPE) {
083            return Float.class;
084        } else if (clazz == Double.TYPE) {
085            return Double.class;
086        } else if (clazz == Short.TYPE) {
087            return Short.class;
088        } else if (clazz == Character.TYPE) {
089            return Character.class;
090        } else if (clazz == Byte.TYPE) {
091            return Byte.class;
092        } else if (clazz == Void.TYPE) {
093            return Void.class;
094        }
095        return clazz;
096    }
097
098
099    public static boolean isArray(Class<?> clazz) {
100        return clazz.isArray()
101                || clazz == int[].class
102                || clazz == long[].class
103                || clazz == short[].class
104                || clazz == float[].class
105                || clazz == double[].class;
106    }
107
108
109    public static <T> T newInstance(Class<T> clazz) {
110        try {
111            Constructor<?> defaultConstructor = null;
112            Constructor<?> otherConstructor = null;
113
114            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
115            for (Constructor<?> constructor : declaredConstructors) {
116                if (constructor.getParameterCount() == 0) {
117                    defaultConstructor = constructor;
118                } else if (Modifier.isPublic(constructor.getModifiers())) {
119                    otherConstructor = constructor;
120                }
121            }
122            if (defaultConstructor != null) {
123                return (T) defaultConstructor.newInstance();
124            } else if (otherConstructor != null) {
125                Class<?>[] parameterTypes = otherConstructor.getParameterTypes();
126                Object[] parameters = new Object[parameterTypes.length];
127                for (int i = 0; i < parameterTypes.length; i++) {
128                    if (parameterTypes[i].isPrimitive()) {
129                        parameters[i] = ConvertUtil.getPrimitiveDefaultValue(parameterTypes[i]);
130                    } else {
131                        parameters[i] = null;
132                    }
133                }
134                return (T) otherConstructor.newInstance(parameters);
135            }
136            throw new IllegalArgumentException("the class \"" + clazz.getName() + "\" has no constructor.");
137        } catch (Exception e) {
138            throw new RuntimeException("Can not newInstance class: " + clazz.getName());
139        }
140    }
141
142
143    public static <T> T newInstance(Class<T> clazz, Object... paras) {
144        try {
145            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
146            for (Constructor<?> constructor : constructors) {
147                if (isMatchedParas(constructor, paras)) {
148                    Object ret = constructor.newInstance(paras);
149                    return (T) ret;
150                }
151            }
152            throw new IllegalArgumentException("Can not find constructor by paras: \"" + Arrays.toString(paras) + "\" in class[" + clazz.getName() + "]");
153        } catch (Exception e) {
154            e.printStackTrace();
155        }
156
157        return null;
158    }
159
160
161    private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) {
162        if (constructor.getParameterCount() == 0) {
163            return paras == null || paras.length == 0;
164        }
165
166        if (constructor.getParameterCount() > 0
167                && (paras == null || paras.length != constructor.getParameterCount())) {
168            return false;
169        }
170
171        Class<?>[] parameterTypes = constructor.getParameterTypes();
172        for (int i = 0; i < parameterTypes.length; i++) {
173            Class<?> parameterType = parameterTypes[i];
174            Object paraObject = paras[i];
175            if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
176                return false;
177            }
178        }
179
180        return true;
181    }
182
183
184    public static List<Field> getAllFields(Class<?> clazz) {
185        List<Field> fields = new ArrayList<>();
186        doGetFields(clazz, fields, null, false);
187        return fields;
188    }
189
190    public static List<Field> getAllFields(Class<?> clazz, Predicate<Field> predicate) {
191        List<Field> fields = new ArrayList<>();
192        doGetFields(clazz, fields, predicate, false);
193        return fields;
194    }
195
196    public static Field getFirstField(Class<?> clazz, Predicate<Field> predicate) {
197        List<Field> fields = new ArrayList<>();
198        doGetFields(clazz, fields, predicate, true);
199        return fields.isEmpty() ? null : fields.get(0);
200    }
201
202    private static void doGetFields(Class<?> clazz, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
203        if (clazz == null || clazz == Object.class) {
204            return;
205        }
206
207        Field[] declaredFields = clazz.getDeclaredFields();
208        for (Field declaredField : declaredFields) {
209            if (predicate == null || predicate.test(declaredField)) {
210                fields.add(declaredField);
211                if (firstOnly) {
212                    break;
213                }
214            }
215        }
216
217        if (firstOnly && !fields.isEmpty()) {
218            return;
219        }
220
221        doGetFields(clazz.getSuperclass(), fields, predicate, firstOnly);
222    }
223
224    public static List<Method> getAllMethods(Class<?> clazz) {
225        List<Method> methods = new ArrayList<>();
226        doGetMethods(clazz, methods, null, false);
227        return methods;
228    }
229
230    public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method> predicate) {
231        List<Method> methods = new ArrayList<>();
232        doGetMethods(clazz, methods, predicate, false);
233        return methods;
234    }
235
236    public static Method getFirstMethod(Class<?> clazz, Predicate<Method> predicate) {
237        List<Method> methods = new ArrayList<>();
238        doGetMethods(clazz, methods, predicate, true);
239        return methods.isEmpty() ? null : methods.get(0);
240    }
241
242
243    private static void doGetMethods(Class<?> clazz, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
244        if (clazz == null || clazz == Object.class) {
245            return;
246        }
247
248        Method[] declaredMethods = clazz.getDeclaredMethods();
249        for (Method method : declaredMethods) {
250            if (predicate == null || predicate.test(method)) {
251                methods.add(method);
252                if (firstOnly) {
253                    break;
254                }
255            }
256        }
257
258        if (firstOnly && !methods.isEmpty()) {
259            return;
260        }
261
262        doGetMethods(clazz.getSuperclass(), methods, predicate, firstOnly);
263    }
264
265
266    private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
267        final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());
268        return (Class<T>) proxyClass.getInterfaces()[0];
269    }
270
271}