All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.binis.codegen.tools.Reflection Maven / Gradle / Ivy

There is a newer version: 1.2.22
Show newest version
package net.binis.codegen.tools;

/*-
 * #%L
 * code-generator-core
 * %%
 * Copyright (C) 2021 - 2024 Binis Belev
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

@Slf4j
public abstract class Reflection {

    protected static ClassLoader loader;

    protected static final Unsafe unsafe = getUnsafe();

    protected static final Method declaredFields = findMethod("getDeclaredFields0", Class.class, boolean.class);

    protected static final Long offset = getFirstFieldOffset();

    protected Reflection() {
        //Do nothing
    }

    @SuppressWarnings("unchecked")
    public static  Class loadClass(String className) {
        try {
            return Objects.nonNull(loader) ? (Class) loader.loadClass(className) : (Class) Class.forName(className);
        } catch (Throwable e) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static  Class loadClass(ClassLoader loader, String className) {
        try {
            return (Class) loader.loadClass(className);
        } catch (Throwable e) {
            return null;
        }
    }

    public static Class loadNestedClass(String className) {
        var result = loadClass(className);
        if (isNull(result)) {
            var builder = new StringBuilder(className);
            var idx = builder.lastIndexOf(".");
            while (idx >= 0) {
                builder.setCharAt(idx, '$');
                result = loadClass(builder.toString());
                if (nonNull(result)) {
                    return result;
                }
                idx = builder.lastIndexOf(".");
            }
        }
        return result;
    }

    public static Class loadClass(String className, ClassLoader loader) {
        try {
            return loader.loadClass(className);
        } catch (Throwable e) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static  T instantiate(Class cls, Object... params) {
        return (T) findConstructor(cls, params).newInstance(params);
    }

    public static Constructor findConstructor(Class cls, Object... params) {
        for (var constructor : cls.getDeclaredConstructors()) {
            if (constructor.getParameterCount() == params.length) {
                var types = constructor.getParameterTypes();
                var match = true;
                for (var i = 0; i < params.length; i++) {
                    if ((isNull(params[i]) && types[i].isPrimitive()) || !compatible(types[i], params[i])) {
                        match = false;
                        break;
                    }
                }
                if (match) {
                    if (!Modifier.isPublic(constructor.getModifiers())) {
                        if (!constructor.trySetAccessible()) {
                            setAccessible(constructor);
                        }
                    }
                    return constructor;
                }
            }
        }
        throw new UnsupportedOperationException("Unable to find proper constructor for class " + cls.getCanonicalName());
    }

    public static boolean compatible(Class type, Object obj) {
        if (nonNull(obj)) {
            var isClass = obj instanceof Class c;
            var aClass = isClass ? (Class) obj : obj.getClass();
            if (type.isPrimitive()) {
                return type.equals(TypeUtils.getPrimitiveType(aClass));
            } else {
                return (isClass && type.isInstance(aClass)) || type.isAssignableFrom(aClass);
            }
        } else {
            return !type.isPrimitive();
        }
    }

    public static Class initialize(String cls, Object... params) {
        return instantiate(loadClass(cls), params).getClass();
    }

    public static Field findField(Class cls, String name) {
        try {
            return cls.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            try {
                return cls.getField(name);
            } catch (NoSuchFieldException ex) {
                var fields = (Field[]) invoke(declaredFields, cls, false);
                for (var field : fields) {
                    if (field.getName().equals(name)) {
                        return field;
                    }
                }
                if (nonNull(cls.getSuperclass())) {
                    return findField(cls.getSuperclass(), name);
                }
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static  T getFieldValueUnsafe(Object obj, String name) {
        try {
            var field = findField(obj.getClass(), name);
            return (T) unsafe.getObject(obj, unsafe.objectFieldOffset(field));
        } catch (Exception e) {
            log.error("Unable to get value for field {} of {}", name, obj.getClass().getName(), e);
            return null;
        }
    }

    public static  T getFieldValue(Object obj, String name) {
        return getFieldValue(obj.getClass(), obj, name);
    }

    public static void setFieldValue(Class cls, Object obj, String name, Object value) {
        try {
            var field = findField(cls, name);
            if (!field.trySetAccessible()) {
                setAccessible(field);
            }
            field.set(obj, value);
        } catch (Exception e) {
            log.error("Unable to set value for field {} of {}", name, obj.getClass().getName(), e);
        }
    }

    public static  T setFieldValue(T obj, String name, Object value) {
        setFieldValue(obj.getClass(), obj, name, value);
        return obj;
    }

    public static  T setFieldValue(Field field, T obj, Object value) {
        try {
            if (!field.trySetAccessible()) {
                setAccessible(field);
            }
            field.set(obj, value);
        } catch (Exception e) {
            log.error("Unable to set value for field {} of {}", field.getName(), obj.getClass().getName(), e);
        }
        return obj;
    }

    public static  T getFieldValue(Class cls, Object obj, String name) {
        return getFieldValue(findField(cls, name), obj, name);
    }

    @SuppressWarnings("unchecked")
    protected static  T getFieldValue(Field field, Object obj, String name) {
        if (nonNull(field)) {
            try {
                if (!field.trySetAccessible()) {
                    setAccessible(field);
                }
                return (T) field.get(obj);
            } catch (Exception e) {
                log.error("Unable to get value for field {} of {}", name, obj.getClass().getName(), e);
            }
        }
        return null;
    }

    public static  T getFieldValue(Field field, Object obj) {
        return getFieldValue(field, obj, field.getName());
    }

    @SuppressWarnings("unchecked")
    public static  T getStaticFieldValue(Class cls, String name) {
        try {
            var field = findField(cls, name);
            if (!field.trySetAccessible()) {
                setAccessible(field);
            }
            return (T) field.get(null);
        } catch (Exception e) {
            log.error("Unable to get value for field {} of {}", name, cls.getName(), e);
            return null;
        }
    }


    public static void withLoader(ClassLoader loader, Runnable task) {
        try {
            Reflection.loader = loader;
            task.run();
        } finally {
            Reflection.loader = null;
        }
    }

    public static boolean isGetter(Method method) {
        var name = method.getName();
        return !method.getReturnType().equals(void.class) && method.getParameterCount() == 0 &&
                (((name.startsWith("get") && name.length() > 3) && (Character.isUpperCase(name.charAt(3)))) ||
                        ((name.startsWith("is") && name.length() > 2) && (Character.isUpperCase(name.charAt(2)))));
    }

    public static boolean isSetter(Method method) {
        var name = method.getName();
        return method.getReturnType().equals(void.class) && method.getParameterCount() == 1 &&
                (name.startsWith("set") && name.length() > 3) && (Character.isUpperCase(name.charAt(3)));
    }

    @SuppressWarnings("unchecked")
    public static Method findMethod(String name, Class cls, Class... params) {
        Method result = null;
        try {
            result = cls.getDeclaredMethod(name, params);
        } catch (Exception e) {
            try {
                result = cls.getMethod(name, params);
            } catch (Exception ex) {
                //Do nothing
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public static  T invoke(Method m, Object instance, Object... args) {
        try {
            if (!m.trySetAccessible()) {
                setAccessible(m);
            }
            return (T) m.invoke(instance, args);
        } catch (Exception e) {
            return null;
        }
    }

    public static  T invoke(String name, Object instance, Object... args) {
        try {
            var m = findMethod(name, instance.getClass(), Arrays.stream(args).map(Object::getClass).toArray(Class[]::new));
            if (nonNull(m)) {
                return invoke(m, instance, args);
            }
        } catch (Exception e) {
            //Do nothing
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static  T invokeWithException(Method m, Object instance, Object... args) {
        try {
            return (T) m.invoke(instance, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw e;
        }
    }

    @SuppressWarnings("unchecked")
    public static  T invokeStatic(Method m, Object... args) {
        try {
            if (!m.trySetAccessible()) {
                setAccessible(m);
            }
            return (T) m.invoke(null, args);
        } catch (Exception e) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static  T invokeStaticWithException(Method m, Object... args) {
        try {
            return (T) m.invoke(null, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw e;
        }
    }

    public static  T invokeStatic(String name, Class cls, Object... args) {
        try {
            var m = findMethod(name, cls, Arrays.stream(args).map(Object::getClass).toArray(Class[]::new));
            if (nonNull(m)) {
                return invokeStatic(m, args);
            }
        } catch (Exception e) {
            //Do nothing
        }
        return null;
    }

    public static List findMethods(Class cls, Predicate filter) {
        return Arrays.stream(cls.getMethods())
                .filter(filter)
                .toList();
    }

    public static Unsafe getUnsafe() {
        try {
            var theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.trySetAccessible();
            return (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            return null;
        }
    }

    public static void setAccessible(Member m) {
        try {
            unsafe.putBooleanVolatile(m, offset, true);
        } catch (Exception e) {
        }
    }

    protected static long getFirstFieldOffset() {
        try {
            return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first"));
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        }
    }

    static class Parent {
        boolean first;

        private Parent() {
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy