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

com.landawn.abacus.util.Reflection Maven / Gradle / Ivy

There is a newer version: 1.8.1
Show newest version
/*
 * Copyright (C) 2017 HaiYang Li
 *
 * 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.
 */

package com.landawn.abacus.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Add reflectasm library to build path for better performance.
 * 
 * @param 
 * @since 0.8
 * 
 * @author haiyangl
 */
public final class Reflection {
    @SuppressWarnings("rawtypes")
    static final Class[] EMPTY_CLASSES = new Class[0];

    static final boolean isReflectASMAvailable;

    static {
        boolean tmp = true;

        try {
            ClassUtil.forClass("com.esotericsoftware.reflectasm.ConstructorAccess");
            ClassUtil.forClass("com.esotericsoftware.reflectasm.FieldAccess");
            ClassUtil.forClass("com.esotericsoftware.reflectasm.MethodAccess");
        } catch (Throwable e) {
            tmp = false;
        }

        isReflectASMAvailable = tmp;
    }

    static final Map, Map> clsFieldPool = new ConcurrentHashMap<>();
    static final Map, Map[]>, Constructor>> clsConstructorPool = new ConcurrentHashMap<>();
    static final Map, Map[]>, Method>>> clsMethodPool = new ConcurrentHashMap<>();

    private final Class cls;
    private final T target;
    private ReflectASM reflectASM;

    Reflection(Class cls, T target) {
        this.cls = cls;
        this.target = target;
        this.reflectASM = isReflectASMAvailable ? new ReflectASM<>(cls, target) : null;
    }

    public static  Reflection on(String clsName) {
        return on((Class) ClassUtil.forClass(clsName));
    }

    public static  Reflection on(Class cls) {
        return new Reflection<>(cls, null);
    }

    public static  Reflection on(T target) {
        return new Reflection<>((Class) target.getClass(), target);
    }

    public Reflection _new() {
        return new Reflection<>(cls, N.newInstance(cls));
    }

    @SafeVarargs
    public final Reflection _new(Object... args) {
        if (N.isNullOrEmpty(args)) {
            return _new();
        }

        final Constructor constructor = getDeclaredConstructor(cls, getTypes(args));

        if (Modifier.isPublic(constructor.getModifiers()) == false && constructor.isAccessible() == false) {
            constructor.setAccessible(true);
        }

        return new Reflection<>(cls, ClassUtil.invokeConstructor(constructor, args));
    }

    public T instance() {
        return target;
    }

    public  V get(String fieldName) {
        if (reflectASM != null) {
            return reflectASM.get(fieldName);
        } else {
            try {
                final Field field = getField(fieldName);

                if (Modifier.isPublic(field.getModifiers()) == false && field.isAccessible() == false) {
                    field.setAccessible(true);
                }

                return (V) field.get(target);
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                throw N.toRuntimeException(e);
            }
        }
    }

    public Reflection set(String fieldName, Object value) {
        if (reflectASM != null) {
            reflectASM.set(fieldName, value);
        } else {
            try {
                final Field field = getField(fieldName);

                if (Modifier.isPublic(field.getModifiers()) == false && field.isAccessible() == false) {
                    field.setAccessible(true);
                }

                field.set(target, value);
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                throw N.toRuntimeException(e);
            }
        }

        return this;
    }

    @SafeVarargs
    public final  V invoke(String methodName, Object... args) {
        if (reflectASM != null) {
            return reflectASM.invoke(methodName, args);
        } else {
            try {
                final Method method = getDeclaredMethod(cls, methodName, getTypes(args));

                if (Modifier.isPublic(method.getModifiers()) == false && method.isAccessible() == false) {
                    method.setAccessible(true);
                }

                return (V) method.invoke(target, args);
            } catch (SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
                throw N.toRuntimeException(e);
            }
        }
    }

    @SafeVarargs
    public final Reflection invoke2(String methodName, Object... args) {
        if (reflectASM != null) {
            reflectASM.invoke2(methodName, args);
        } else {
            invoke(methodName, args);
        }

        return this;
    }

    private Field getField(String fieldName) throws NoSuchFieldException {
        Map fieldPool = clsFieldPool.get(cls);

        if (fieldPool == null) {
            fieldPool = new ConcurrentHashMap<>();
            clsFieldPool.put(cls, fieldPool);
        }

        Field field = fieldPool.get(fieldName);

        if (field == null) {
            field = cls.getField(fieldName);
            fieldPool.put(fieldName, field);
        }

        return field;
    }

    private Constructor getDeclaredConstructor(final Class cls, final Class[] argTypes) throws SecurityException {
        Map[]>, Constructor> constructorPool = clsConstructorPool.get(cls);

        if (constructorPool == null) {
            constructorPool = new ConcurrentHashMap<>();
            clsConstructorPool.put(cls, constructorPool);
        }

        final Wrapper[]> key = Wrapper.of(argTypes);
        Constructor result = constructorPool.get(key);

        if (result == null) {
            try {
                result = cls.getDeclaredConstructor(argTypes);
            } catch (NoSuchMethodException e) {
                for (Constructor constructor : cls.getDeclaredConstructors()) {
                    final Class[] paramTypes = constructor.getParameterTypes();

                    if (paramTypes != null && paramTypes.length == argTypes.length) {
                        for (int i = 0, len = paramTypes.length; i < len; i++) {
                            if (argTypes[i] == null || paramTypes[i].isAssignableFrom(argTypes[i]) || wrap(paramTypes[i]).isAssignableFrom(wrap(argTypes[i]))) {
                                if (i == len - 1) {
                                    result = constructor;
                                }
                            }
                        }
                    }

                    if (result != null) {
                        break;
                    }
                }
            }

            if (result == null) {
                throw new RuntimeException("No constructor found with parameter types: " + N.toString(argTypes));
            }

            constructorPool.put(key, result);
        }

        return (Constructor) result;
    }

    private Method getDeclaredMethod(final Class cls, final String methodName, final Class[] argTypes) throws SecurityException {
        Map[]>, Method>> methodPool = clsMethodPool.get(cls);

        if (methodPool == null) {
            methodPool = new ConcurrentHashMap<>();
            clsMethodPool.put(cls, methodPool);
        }

        Map[]>, Method> argsMethodPool = methodPool.get(methodName);

        if (argsMethodPool == null) {
            argsMethodPool = new ConcurrentHashMap<>();
            methodPool.put(methodName, argsMethodPool);
        }

        final Wrapper[]> key = Wrapper.of(argTypes);
        Method result = argsMethodPool.get(key);

        if (result == null) {
            try {
                result = cls.getDeclaredMethod(methodName, argTypes);
            } catch (NoSuchMethodException e) {
                for (Method method : cls.getDeclaredMethods()) {
                    final Class[] paramTypes = method.getParameterTypes();

                    if (method.getName().equals(methodName) && (paramTypes != null && paramTypes.length == argTypes.length)) {
                        for (int i = 0, len = paramTypes.length; i < len; i++) {
                            if (argTypes[i] == null || paramTypes[i].isAssignableFrom(argTypes[i]) || wrap(paramTypes[i]).isAssignableFrom(wrap(argTypes[i]))) {
                                if (i == len - 1) {
                                    result = method;
                                }
                            }
                        }
                    }

                    if (result != null) {
                        break;
                    }
                }

                if (result == null) {
                    throw new RuntimeException("No method found by name: " + methodName + " with parameter types: " + N.toString(argTypes));
                }

                argsMethodPool.put(key, result);
            }
        }

        return result;
    }

    private Class[] getTypes(Object... values) {
        if (N.isNullOrEmpty(values)) {
            return EMPTY_CLASSES;
        }

        final Class[] result = new Class[values.length];

        for (int i = 0; i < values.length; i++) {
            result[i] = values[i] == null ? null : values[i].getClass();
        }

        return result;
    }

    private Class wrap(final Class cls) {
        return N.isPrimitive(cls) ? N.wrapperOf(cls) : cls;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy