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

com.devops4j.reflection4j.reflector.DefaultReflector Maven / Gradle / Ivy

There is a newer version: 1.0.0-PRE1
Show newest version
package com.devops4j.reflection4j.reflector;

import com.devops4j.logtrace4j.ErrorContextFactory;
import com.devops4j.reflection4j.Invoker;
import com.devops4j.reflection4j.Reflector;
import com.devops4j.reflection4j.invoker.GetFieldInvoker;
import com.devops4j.reflection4j.invoker.MethodInvoker;
import com.devops4j.reflection4j.invoker.SetFieldInvoker;
import com.devops4j.reflection4j.property.PropertyNamer;

import java.lang.reflect.*;
import java.util.*;

/**
 * 默认实现的反射器接口
 */
public class DefaultReflector implements Reflector {
    static final Set IGNORE_FIELD = new HashSet();

    static final String[] EMPTY_STRING_ARRAY = new String[0];

    Class type;
    String[] readablePropertyNames = EMPTY_STRING_ARRAY;
    String[] writablePropertyNames = EMPTY_STRING_ARRAY;
    Map setMethods = new HashMap();
    Map getMethods = new HashMap();
    Map> setTypes = new HashMap();
    Map> getTypes = new HashMap();
    Map fields = new HashMap();
    Constructor defaultConstructor;

    static {
        IGNORE_FIELD.add("serialPersistentFields");
        IGNORE_FIELD.add("CASE_INSENSITIVE_ORDER");
        IGNORE_FIELD.add("bytes");
        IGNORE_FIELD.add("class");
        IGNORE_FIELD.add("value");
        IGNORE_FIELD.add("hash");
        IGNORE_FIELD.add("empty");
        IGNORE_FIELD.add("BYTES");
        IGNORE_FIELD.add("DigitTens");
        IGNORE_FIELD.add("DigitOnes");
        IGNORE_FIELD.add("sizeTable");
        IGNORE_FIELD.add("SIZE");
        IGNORE_FIELD.add("digits");
        IGNORE_FIELD.add("MAX_VALUE");
        IGNORE_FIELD.add("MIN_VALUE");
        IGNORE_FIELD.add("TYPE");
    }

    public DefaultReflector(Class clazz) {
        type = clazz;
        addDefaultConstructor(clazz);
        addGetMethods(clazz);
        addSetMethods(clazz);
        addFields(clazz);
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        writablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    }

    void addDefaultConstructor(Class clazz) {
        Constructor[] consts = clazz.getDeclaredConstructors();
        boolean found = false;
        for (Constructor constructor : consts) {
            if (constructor.getParameterTypes().length == 0) {
                if (canAccessPrivateMethods()) {
                    try {
                        constructor.setAccessible(true);
                    } catch (Exception e) {
                        // Ignored. This is only a final precaution, nothing we can do.
                    }
                }

                if (constructor.isAccessible()) {
                    this.defaultConstructor = constructor;
                }
                found = true;
            }
        }
    }

    void addGetMethods(Class cls) {
        Map> conflictingGetters = new HashMap>();
        Method[] methods = getClassMethods(cls);
        for (Method method : methods) {
            String name = method.getName();
            if (PropertyNamer.isGetter(name)) {
                name = PropertyNamer.methodToProperty(name);
                if (IGNORE_FIELD.contains(name)) {
                    continue;
                }
                //GETTER为无参
                if (method.getParameterTypes().length == 0) {
                    addMethodConflict(conflictingGetters, name, method);
                }
            }
        }
        resolveGetterConflicts(conflictingGetters);
    }

    void resolveGetterConflicts(Map> conflictingGetters) {
        for (String propName : conflictingGetters.keySet()) {
            List getters = conflictingGetters.get(propName);
            Iterator iterator = getters.iterator();
            Method firstMethod = iterator.next();
            if (getters.size() == 1) {
                addGetMethod(propName, firstMethod);
            } else {
                Method getter = firstMethod;
                Class getterType = firstMethod.getReturnType();
                while (iterator.hasNext()) {
                    Method method = iterator.next();
                    Class methodType = method.getReturnType();
                    if (methodType.equals(getterType)) {
                        throw ErrorContextFactory.instance().message("类'{}'中存在冲突的Getter方法的重载方法'{}'.不符合JavaBean的规范", firstMethod.getDeclaringClass(), propName).runtimeException();
                    } else if (methodType.isAssignableFrom(getterType)) {
                        // OK getter type is descendant
                    } else if (getterType.isAssignableFrom(methodType)) {
                        getter = method;
                        getterType = methodType;
                    } else {
                        throw ErrorContextFactory.instance().message("类'{}'中存在冲突的Getter方法的重载方法'{}'.不符合JavaBean的规范", firstMethod.getDeclaringClass(), propName).runtimeException();
                    }
                }
                addGetMethod(propName, getter);
            }
        }
    }

    void addGetMethod(String name, Method method) {
        if (isValidPropertyName(name)) {
            getMethods.put(name, new MethodInvoker(method));
            getTypes.put(name, method.getReturnType());
        }
    }

    void addSetMethods(Class cls) {
        Map> conflictingSetters = new HashMap>();
        Method[] methods = getClassMethods(cls);
        for (Method method : methods) {
            String name = method.getName();
            if (PropertyNamer.isSetter(name)) {
                name = PropertyNamer.methodToProperty(name);
                if (IGNORE_FIELD.contains(name)) {
                    continue;
                }
                //SETTER为1个参数
                if (method.getParameterTypes().length == 1) {
                    addMethodConflict(conflictingSetters, name, method);
                }
            }
        }
        resolveSetterConflicts(conflictingSetters);
    }

    /**
     * 增加相互干扰的方法
     *
     * @param conflictingMethods
     * @param name
     * @param method
     */
    void addMethodConflict(Map> conflictingMethods, String name, Method method) {
        List list = conflictingMethods.get(name);
        if (list == null) {
            list = new ArrayList();
            conflictingMethods.put(name, list);
        }
        list.add(method);
    }

    void resolveSetterConflicts(Map> conflictingSetters) {
        for (String propName : conflictingSetters.keySet()) {
            List setters = conflictingSetters.get(propName);
            Method firstMethod = setters.get(0);
            if (setters.size() == 1) {
                addSetMethod(propName, firstMethod);
            } else {
                Class expectedType = getTypes.get(propName);
                if (expectedType == null) {
                    throw ErrorContextFactory.instance().message("类'{}'中存在冲突的Setter方法的重载方法'{}'.不符合JavaBean的规范", firstMethod.getDeclaringClass(), propName).runtimeException();
                } else {
                    Iterator methods = setters.iterator();
                    Method setter = null;
                    while (methods.hasNext()) {
                        Method method = methods.next();
                        if (method.getParameterTypes().length == 1
                                && expectedType.equals(method.getParameterTypes()[0])) {
                            setter = method;
                            break;
                        }
                    }
                    if (setter == null) {
                        throw ErrorContextFactory.instance().message("类'{}'中存在冲突的Setter方法的重载方法'{}'.不符合JavaBean的规范", firstMethod.getDeclaringClass(), propName).runtimeException();
                    }
                    addSetMethod(propName, setter);
                }
            }
        }
    }

    void addSetMethod(String name, Method method) {
        if (isValidPropertyName(name)) {
            setMethods.put(name, new MethodInvoker(method));
            setTypes.put(name, method.getParameterTypes()[0]);
        }
    }

    void addFields(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (canAccessPrivateMethods()) {
                try {
                    field.setAccessible(true);
                } catch (Exception e) {
                    // Ignored. This is only a final precaution, nothing we can do.
                }
            }
            if (IGNORE_FIELD.contains(field.getName())) {
                continue;
            }
            if (field.isAccessible()) {
                if (!setMethods.containsKey(field.getName())) {
                    // issue #379 - removed the check for final because JDK 1.5 allows
                    // modification of final fields through reflection4j (JSR-133). (JGB)
                    // pr #16 - final static can only be set by the classloader
                    int modifiers = field.getModifiers();
                    if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
                        addSetField(field);
                    }
                }
                if (!getMethods.containsKey(field.getName())) {
                    addGetField(field);
                }
                this.fields.put(field.getName(), field);
            }
        }
        //将父类的属性加入
        if (clazz.getSuperclass() != null) {
            addFields(clazz.getSuperclass());
        }
    }

    void addSetField(Field field) {
        if (isValidPropertyName(field.getName())) {
            setMethods.put(field.getName(), new SetFieldInvoker(field));
            setTypes.put(field.getName(), field.getType());
        }
    }

    void addGetField(Field field) {
        if (isValidPropertyName(field.getName())) {
            getMethods.put(field.getName(), new GetFieldInvoker(field));
            getTypes.put(field.getName(), field.getType());
        }
    }

    boolean isValidPropertyName(String name) {
        return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
    }

    Method[] getClassMethods(Class cls) {
        Map uniqueMethods = new HashMap();
        Class currentClass = cls;
        while (currentClass != null) {
            addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
            // we also need to look for interface methods -
            // because the class may be abstract
            Class[] interfaces = currentClass.getInterfaces();
            for (Class anInterface : interfaces) {
                addUniqueMethods(uniqueMethods, anInterface.getMethods());
            }

            currentClass = currentClass.getSuperclass();
        }

        Collection methods = uniqueMethods.values();

        return methods.toArray(new Method[methods.size()]);
    }

    void addUniqueMethods(Map uniqueMethods, Method[] methods) {
        for (Method currentMethod : methods) {
            if (!currentMethod.isBridge()) {
                String signature = getSignature(currentMethod);
                // check to see if the method is already known
                // if it is known, then an extended class must have
                // overridden a method
                if (!uniqueMethods.containsKey(signature)) {
                    if (canAccessPrivateMethods()) {
                        try {
                            currentMethod.setAccessible(true);
                        } catch (Exception e) {
                            // Ignored. This is only a final precaution, nothing we can do.
                        }
                    }

                    uniqueMethods.put(signature, currentMethod);
                }
            }
        }
    }

    String getSignature(Method method) {
        StringBuilder sb = new StringBuilder();
        Class returnType = method.getReturnType();
        if (returnType != null) {
            sb.append(returnType.getName()).append('#');
        }
        sb.append(method.getName());
        Class[] parameters = method.getParameterTypes();
        for (int i = 0; i < parameters.length; i++) {
            if (i == 0) {
                sb.append(':');
            } else {
                sb.append(',');
            }
            sb.append(parameters[i].getName());
        }
        return sb.toString();
    }

    private static boolean canAccessPrivateMethods() {
        try {
            SecurityManager securityManager = System.getSecurityManager();
            if (null != securityManager) {
                securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
            }
        } catch (SecurityException e) {
            return false;
        }
        return true;
    }

    public Class getType() {
        return type;
    }

    public Constructor getDefaultConstructor() {
        if (defaultConstructor != null) {
            return defaultConstructor;
        } else {
            throw ErrorContextFactory.instance().message("There is no default constructor for {}", type).runtimeException();
        }
    }

    public boolean hasDefaultConstructor() {
        return defaultConstructor != null;
    }

    public Invoker getMethodInvoker(String name, Class... classes) {
        return null;
    }

    public Invoker getSetter(String propertyName) {
        Invoker method = setMethods.get(propertyName);
        if (method == null) {
            throw ErrorContextFactory.instance().message("There is no setter for property named '{}' in '{}'", propertyName, type).runtimeException();
        }
        return method;
    }

    public Invoker getGetter(String propertyName) {
        Invoker method = getMethods.get(propertyName);
        if (method == null) {
            throw ErrorContextFactory.instance().message("There is no getter for property named '{}' in '{}'", propertyName, type).runtimeException();
        }
        return method;
    }

    public Class getSetterType(String propertyName) {
        Class clazz = setTypes.get(propertyName);
        if (clazz == null) {
            throw ErrorContextFactory.instance().message("There is no setter for property named '{}' in '{}'", propertyName, type).runtimeException();
        }
        return clazz;
    }

    public Class getGetterType(String propertyName) {
        Class clazz = getTypes.get(propertyName);
        if (clazz == null) {
            throw ErrorContextFactory.instance().message("There is no getter for property named '{}' in '{}'", propertyName, type).runtimeException();
        }
        return clazz;
    }

    public Collection getGettablePropertyNames() {
        return Arrays.asList(readablePropertyNames);
    }

    public Collection getSettablePropertyNames() {
        return Arrays.asList(writablePropertyNames);
    }

    public boolean hasSetter(String propertyName) {
        return setMethods.keySet().contains(propertyName);
    }

    public boolean hasGetter(String propertyName) {
        return getMethods.keySet().contains(propertyName);
    }

    public boolean hasProperty(String propertyName) {
        return fields.containsKey(propertyName);
    }

    public Field getField(String propertyName) {
        return fields.get(propertyName);
    }

    public List getFields() {
        return Collections.unmodifiableList(new ArrayList(fields.values()));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy