001/*
002 *  Copyright (c) 2023-2026, Agents-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.agentsflex.core.util;
017
018import java.lang.reflect.*;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import java.util.function.Predicate;
023
024/**
025 * 类实例创建者创建者
026 *
027 * @author michael
028 * @date 17/3/21
029 */
030public class ClassUtil {
031
032    private ClassUtil() {
033    }
034
035    private static final String[] OBJECT_METHODS = new String[]{
036        "toString",
037        "getClass",
038        "equals",
039        "hashCode",
040        "wait",
041        "notify",
042        "notifyAll",
043        "clone",
044        "finalize"
045    };
046
047    //proxy frameworks
048    private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
049        // cglib
050        , "org.springframework.cglib.proxy.Factory"
051
052        // javassist
053        , "javassist.util.proxy.ProxyObject"
054        , "org.apache.ibatis.javassist.util.proxy.ProxyObject");
055    private static final String ENHANCER_BY = "$$EnhancerBy";
056    private static final String JAVASSIST_BY = "_$$_";
057
058    public static boolean isProxy(Class<?> clazz) {
059        for (Class<?> cls : clazz.getInterfaces()) {
060            if (PROXY_CLASS_NAMES.contains(cls.getName())) {
061                return true;
062            }
063        }
064        //java proxy
065        return Proxy.isProxyClass(clazz);
066    }
067
068    private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
069        final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());
070        return (Class<T>) proxyClass.getInterfaces()[0];
071    }
072
073    public static <T> Class<T> getUsefulClass(Class<T> clazz) {
074        if (isProxy(clazz)) {
075            return getJdkProxySuperClass(clazz);
076        }
077
078        //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello   -------> Guice
079        //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158  ----> CGLIB
080        //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist
081        final String name = clazz.getName();
082        if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
083            return (Class<T>) clazz.getSuperclass();
084        }
085
086        return clazz;
087    }
088
089
090    public static Class<?> getWrapType(Class<?> clazz) {
091        if (clazz == null || !clazz.isPrimitive()) {
092            return clazz;
093        }
094        if (clazz == Integer.TYPE) {
095            return Integer.class;
096        } else if (clazz == Long.TYPE) {
097            return Long.class;
098        } else if (clazz == Boolean.TYPE) {
099            return Boolean.class;
100        } else if (clazz == Float.TYPE) {
101            return Float.class;
102        } else if (clazz == Double.TYPE) {
103            return Double.class;
104        } else if (clazz == Short.TYPE) {
105            return Short.class;
106        } else if (clazz == Character.TYPE) {
107            return Character.class;
108        } else if (clazz == Byte.TYPE) {
109            return Byte.class;
110        } else if (clazz == Void.TYPE) {
111            return Void.class;
112        }
113        return clazz;
114    }
115
116
117    public static boolean isArray(Class<?> clazz) {
118        return clazz.isArray()
119            || clazz == int[].class
120            || clazz == long[].class
121            || clazz == short[].class
122            || clazz == float[].class
123            || clazz == double[].class;
124    }
125
126
127    public static List<Field> getAllFields(Class<?> clazz) {
128        List<Field> fields = new ArrayList<>();
129        doGetFields(clazz, fields, null, false);
130        return fields;
131    }
132
133    public static List<Field> getAllFields(Class<?> clazz, Predicate<Field> predicate) {
134        List<Field> fields = new ArrayList<>();
135        doGetFields(clazz, fields, predicate, false);
136        return fields;
137    }
138
139    public static Field getFirstField(Class<?> clazz, Predicate<Field> predicate) {
140        List<Field> fields = new ArrayList<>();
141        doGetFields(clazz, fields, predicate, true);
142        return fields.isEmpty() ? null : fields.get(0);
143    }
144
145    private static void doGetFields(Class<?> clazz, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
146        if (clazz == null || clazz == Object.class) {
147            return;
148        }
149
150        Field[] declaredFields = clazz.getDeclaredFields();
151        for (Field declaredField : declaredFields) {
152            if (predicate == null || predicate.test(declaredField)) {
153                fields.add(declaredField);
154                if (firstOnly) {
155                    break;
156                }
157            }
158        }
159
160        if (firstOnly && !fields.isEmpty()) {
161            return;
162        }
163
164        doGetFields(clazz.getSuperclass(), fields, predicate, firstOnly);
165    }
166
167    public static List<Method> getAllMethods(Class<?> clazz) {
168        List<Method> methods = new ArrayList<>();
169        doGetMethods(clazz, methods, null, false);
170        return methods;
171    }
172
173    public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method> predicate) {
174        List<Method> methods = new ArrayList<>();
175        doGetMethods(clazz, methods, predicate, false);
176        return methods;
177    }
178
179    public static Method getFirstMethod(Class<?> clazz, Predicate<Method> predicate) {
180        List<Method> methods = new ArrayList<>();
181        doGetMethods(clazz, methods, predicate, true);
182        return methods.isEmpty() ? null : methods.get(0);
183    }
184
185
186    private static void doGetMethods(Class<?> clazz, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
187        if (clazz == null || clazz == Object.class) {
188            return;
189        }
190
191        Method[] declaredMethods = clazz.getDeclaredMethods();
192        for (Method method : declaredMethods) {
193            if (predicate == null || predicate.test(method)) {
194                methods.add(method);
195                if (firstOnly) {
196                    break;
197                }
198            }
199        }
200
201        if (firstOnly && !methods.isEmpty()) {
202            return;
203        }
204
205        doGetMethods(clazz.getSuperclass(), methods, predicate, firstOnly);
206    }
207
208}