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