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 <T> T newInstance(Class<T> clazz) {
099        try {
100            Constructor<?> defaultConstructor = null;
101            Constructor<?> otherConstructor = null;
102
103            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
104            for (Constructor<?> constructor : declaredConstructors) {
105                if (constructor.getParameterCount() == 0) {
106                    defaultConstructor = constructor;
107                } else if (Modifier.isPublic(constructor.getModifiers())) {
108                    otherConstructor = constructor;
109                }
110            }
111            if (defaultConstructor != null) {
112                return (T) defaultConstructor.newInstance();
113            } else if (otherConstructor != null) {
114                Class<?>[] parameterTypes = otherConstructor.getParameterTypes();
115                Object[] parameters = new Object[parameterTypes.length];
116                for (int i = 0; i < parameterTypes.length; i++) {
117                    if (parameterTypes[i].isPrimitive()) {
118                        parameters[i] = ConvertUtil.getPrimitiveDefaultValue(parameterTypes[i]);
119                    } else {
120                        parameters[i] = null;
121                    }
122                }
123                return (T) otherConstructor.newInstance(parameters);
124            }
125            throw new IllegalArgumentException("the class \"" + clazz.getName() + "\" has no constructor.");
126        } catch (Exception e) {
127            throw new RuntimeException("Can not newInstance class: " + clazz.getName());
128        }
129    }
130
131
132    public static <T> T newInstance(Class<T> clazz, Object... paras) {
133        try {
134            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
135            for (Constructor<?> constructor : constructors) {
136                if (isMatchedParas(constructor, paras)) {
137                    Object ret = constructor.newInstance(paras);
138                    return (T) ret;
139                }
140            }
141            throw new IllegalArgumentException("Can not find constructor by paras: \"" + Arrays.toString(paras) + "\" in class[" + clazz.getName() + "]");
142        } catch (Exception e) {
143            e.printStackTrace();
144        }
145
146        return null;
147    }
148
149
150    private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) {
151        if (constructor.getParameterCount() == 0) {
152            return paras == null || paras.length == 0;
153        }
154
155        if (constructor.getParameterCount() > 0
156                && (paras == null || paras.length != constructor.getParameterCount())) {
157            return false;
158        }
159
160        Class<?>[] parameterTypes = constructor.getParameterTypes();
161        for (int i = 0; i < parameterTypes.length; i++) {
162            Class<?> parameterType = parameterTypes[i];
163            Object paraObject = paras[i];
164            if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
165                return false;
166            }
167        }
168
169        return true;
170    }
171
172
173    public static List<Field> getAllFields(Class<?> cl) {
174        List<Field> fields = new ArrayList<>();
175        doGetFields(cl, fields, null);
176        return fields;
177    }
178
179    public static List<Field> getAllFields(Class<?> cl, Predicate<Field> predicate) {
180        List<Field> fields = new ArrayList<>();
181        doGetFields(cl, fields, predicate);
182        return fields;
183    }
184
185    private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate) {
186        if (cl == null || cl == Object.class) {
187            return;
188        }
189
190        Field[] declaredFields = cl.getDeclaredFields();
191        for (Field declaredField : declaredFields) {
192            if (predicate == null || predicate.test(declaredField)) {
193                fields.add(declaredField);
194            }
195        }
196
197        doGetFields(cl.getSuperclass(), fields, predicate);
198    }
199
200    public static List<Method> getAllMethods(Class<?> cl) {
201        List<Method> methods = new ArrayList<>();
202        doGetMethods(cl, methods, null);
203        return methods;
204    }
205
206    public static List<Method> getAllMethods(Class<?> cl, Predicate<Method> predicate) {
207        List<Method> methods = new ArrayList<>();
208        doGetMethods(cl, methods, predicate);
209        return methods;
210    }
211
212
213    private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate) {
214        if (cl == null || cl == Object.class) {
215            return;
216        }
217
218        Method[] declaredMethods = cl.getDeclaredMethods();
219        for (Method method : declaredMethods) {
220            if (predicate == null || predicate.test(method)) {
221                methods.add(method);
222            }
223        }
224
225        doGetMethods(cl.getSuperclass(), methods, predicate);
226    }
227
228}