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

com.gitee.qdbp.staticize.parse.AttrSetter Maven / Gradle / Ivy

There is a newer version: 3.5.18
Show newest version
package com.gitee.qdbp.staticize.parse;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.gitee.qdbp.able.beans.KeyValue;
import com.gitee.qdbp.staticize.annotation.AttrRequired;
import com.gitee.qdbp.staticize.annotation.DynamicAttrSupport;
import com.gitee.qdbp.staticize.annotation.TryEvalString;
import com.gitee.qdbp.staticize.exception.TagException;
import com.gitee.qdbp.staticize.tags.base.ITag;
import com.gitee.qdbp.tools.utils.ConvertTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 属性Setter方法调用工具类
 *
 * @author zhaohuihua
 * @version 20200929
 */
public class AttrSetter {

    private static final String SET = "set";

    /** 单实例 **/
    public static final AttrSetter instance = new AttrSetter();

    public void setAttrValue(ITag tag, String attrName, Object attrValue) {
        MethodInfo methodInfo = getMethodInfo(tag.getClass(), attrName);
        if (methodInfo == null) {
            return;
        }
        Object realValue = attrValue == null ? null : doConvertAttrValue(methodInfo, attrName, attrValue);
        if (realValue == null) {
            if (methodInfo.required) {
                String msg = String.format("Missing a required attribute '%s'.", attrName);
                throw new TagException(msg);
            }
        } else {
            // 参数值与目标类型不符
            // 有可能是根据表达式从上下文动态获取到的对象
            if (!methodInfo.paramType.isAssignableFrom(realValue.getClass())) {
                String src = realValue.getClass().getSimpleName();
                String dest = methodInfo.paramType.getSimpleName();
                String msg = String.format("Attribute %s(%s) not of type '%s'.", attrName, src, dest);
                throw new TagException(msg);
            }
        }

        try {
            methodInfo.setterMethod.invoke(tag, realValue);
        } catch (Exception e) {
            Throwable cause = e.getCause();
            if (e instanceof TagException) {
                throw (TagException) e;
            } else if (cause instanceof TagException) {
                throw (TagException) cause;
            } else {
                throw new TagException("Failed to setting attribute '" + attrName + "'.", e);
            }
        }
    }

    private Object doConvertAttrValue(MethodInfo methodInfo, String attrName, Object attrValue) {
        // 参数值已经是目标类型了, 不需要转换
        if (methodInfo.paramType.isAssignableFrom(attrValue.getClass())) {
            return attrValue;
        }

        if (methodInfo.paramType == String.class) {
            // JDK7下集合转字符串有问题, 数据看不见
            if (attrValue instanceof Collection) {
                return ConvertTools.joinToString((Collection) attrValue, ", ", true);
            } else if (attrValue.getClass().isArray()) {
                return ConvertTools.joinToString((Object[]) attrValue, ", ", true);
            }
        }
        // 调用目标类型的静态方法valueOf()将值转换为目标类型
        try {
            return ValueOf.instance.eval(methodInfo.paramType, attrValue);
        } catch (TagException e) {
            e.prependMessage("Attribute " + attrName);
            throw e;
        } catch (Exception e) {
            String name = methodInfo.paramType.getSimpleName();
            String msg = String.format("Attribute '%s' error converting to type '%s'.", attrName, name);
            throw new TagException(msg, e);
        }
    }

    /** 校验必选字段 **/
    public void validAttrRequireds(Class tagType, List attributes) {
        initCacheSetterMethods(tagType);

        // 校验必选字段
        List requireds = cachedRequiredFields.get(tagType);
        if (requireds == null || requireds.isEmpty()) {
            return;
        }
        // 遍历必选字段, 如果属性中没有就报错
        Map map = KeyValue.toMap(attributes);
        for (String item : requireds) {
            Object value = map.get(item);
            if (VerifyTools.isNotBlank(value)) {
                continue; // 属性表有这个字段了
            }
            throw new TagException(String.format("Missing a required attribute '%s'.", item));
        }
    }

    /** 获取动态参数的容器名称 **/
    public String getDynamicName(Class tagType) {
        initCacheSetterMethods(tagType);
        return cachedDynamicNames.get(tagType);
    }

    public MethodInfo getMethodInfo(Class tagType, String attrName) {
        initCacheSetterMethods(tagType);

        boolean throwOnSetterNotFound = !cachedDynamicNames.containsKey(tagType);
        String key = tagType.getName() + '.' + attrName;
        MethodInfo methodInfo = this.cachedMethods.get(key);
        if (methodInfo == null && throwOnSetterNotFound) {
            throw new TagException("Attribute '" + attrName + "' not supported.");
        }
        return methodInfo;
    }

    public static class MethodInfo {

        private final Method setterMethod;
        private final Class paramType;
        private final boolean required;
        /** String类型的属性值要不要尝试作为表达式解析 **/
        private final boolean tryEvalString;

        public MethodInfo(Method setterMethod, Class paramType, boolean required, boolean tryEvalString) {
            this.setterMethod = setterMethod;
            this.paramType = paramType;
            this.required = required;
            this.tryEvalString = tryEvalString;
        }

        public Class getParamType() {
            return paramType;
        }

        public boolean isRequired() {
            return required;
        }

        public boolean isTryEvalString() {
            return tryEvalString;
        }
    }

    private final Map, ?> initedTypes = new HashMap<>();
    private final Map cachedMethods = new HashMap<>();
    private final Map, List> cachedRequiredFields = new HashMap<>();
    private final Map, String> cachedDynamicNames = new HashMap<>();

    private void initCacheSetterMethods(Class tagType) {
        if (!initedTypes.containsKey(tagType)) {
            doInitCacheSetterMethods(tagType);
        }
    }

    private synchronized void doInitCacheSetterMethods(Class tagType) {
        if (initedTypes.containsKey(tagType)) {
            return;
        }

        DynamicAttrSupport dynamicAttr = tagType.getAnnotation(DynamicAttrSupport.class);
        if (dynamicAttr != null && VerifyTools.isNotBlank(dynamicAttr.value())) {
            cachedDynamicNames.put(tagType, dynamicAttr.value());
        }

        List requiredFields = new ArrayList<>();
        Class clazz = tagType;
        for (; clazz != null; clazz = clazz.getSuperclass()) {
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                // 去掉静态方法
                if (Modifier.isStatic(method.getModifiers())) {
                    continue;
                }
                // 去掉非公有方法
                if (!Modifier.isPublic(method.getModifiers())) {
                    continue;
                }
                // 去掉方法名小于3位的
                String name = method.getName();
                if (name.length() <= SET.length()) {
                    continue;
                }
                // 去掉不是set开头的
                if (!name.startsWith(SET)) {
                    continue;
                }
                // 去掉参数不只一个的
                Class[] paramTypes = method.getParameterTypes();
                if (paramTypes.length != 1) {
                    continue;
                }

                Class paramType = paramTypes[0];
                // 首字母小写
                char first = Character.toLowerCase(name.charAt(SET.length()));
                String suffix = name.substring(SET.length() + 1);
                String fieldName = first + suffix;

                AttrRequired attrRequired = method.getAnnotation(AttrRequired.class);
                boolean required = attrRequired != null;
                if (required) {
                    requiredFields.add(fieldName);
                }
                // 检查@TryEvalString注解
                boolean tryEvalStringEnabled = true;
                TryEvalString tryEvalStringAnnotation = method.getAnnotation(TryEvalString.class);
                if (tryEvalStringAnnotation != null) {
                    tryEvalStringEnabled = tryEvalStringAnnotation.enabled();
                }
                String key = tagType.getName() + '.' + fieldName;
                if (!this.cachedMethods.containsKey(key)) {
                    this.cachedMethods.put(key, new MethodInfo(method, paramType, required, tryEvalStringEnabled));
                }
            }
        }
        cachedRequiredFields.put(tagType, requiredFields.isEmpty() ? null : requiredFields);
        initedTypes.put(tagType, null);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy