zoukankan      html  css  js  c++  java
  • 实现一个支持自定义函数的模板表达式

    工作中需要用到一个支持变量替换和自定义函数的模板表达式,发现现有的开源项目不能满足,于是自己造了个轮子。

    该模板表达式核心就三个文件:

    ExpressionNode.java -- 表达式节点

    public class ExpressionNode {
        /** 模板 **/
        public static final Integer TEMPLATE = 0;
        /** 函数 **/
        public static final Integer FUNCTION = 1;
        /** 变量 **/
        public static final Integer VARIABLE = 2;
        /** 常量 **/
        public static final Integer CONSTANT = 3;
        /** 类型 **/
        private Integer nodeType;
        /** 表达式 **/
        private String expression;
        /** 值 **/
        private Object value;
        /** 参数 **/
        private List<ExpressionNode> arguments;
        /** 变量值类型 **/
        private Integer varType;
        /** 默认值 **/
        private String defVal;
    
        public Integer getNodeType() {
            return nodeType;
        }
    
        public void setNodeType(Integer nodeType) {
            this.nodeType = nodeType;
        }
    
        public String getExpression() {
            return expression;
        }
    
        public void setExpression(String expression) {
            this.expression = expression;
        }
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    
        public List<ExpressionNode> getArguments() {
            return arguments;
        }
    
        public void setArguments(List<ExpressionNode> arguments) {
            this.arguments = arguments;
        }
    
        public Integer getVarType() {
            return varType;
        }
    
        public void setVarType(Integer varType) {
            this.varType = varType;
        }
    
        public String getDefVal() {
            return defVal;
        }
    
        public void setDefVal(String defVal) {
            this.defVal = defVal;
        }
    
        public void addArgument(ExpressionNode argument) {
            if (argument == null) {
                return;
            }
            if (arguments == null) {
                arguments = new ArrayList<ExpressionNode>();
            }
            arguments.add(argument);
        }
    
        public List<Object> getArgumentValues() {
            List<Object> values = new ArrayList<Object>();
            if (arguments != null) {
                arguments.forEach(arg -> {
                    values.add(arg.getValue());
                });
            }
            return values;
        }
    
        @Override
        public String toString() {
            return "ExpressionNode [nodeType=" + nodeType + ", expression=" + expression + ", value=" + value
                    + ", arguments=" + arguments + ", varType=" + varType + ", defVal=" + defVal + "]";
        }
    
    }
    View Code

    ExpressionParser.java -- 表达式解析器

    public class ExpressionParser {
        public static final String EXP_FUNCTION = "\$(?<fun>[a-zA-Z0-9_]+)\((?<args>.*?)\)";
        public static final String EXP_VARIABLE = "#\{(?<var>[a-zA-Z0-9_.]+)(\:(?<type>[a-z]+))?(\?(?<def>.*?))?\}";
        private static final String COMMA = "\s*,\s*";
        private static final Integer NOT_SUPPORTED_TYPE = -1;
        private Pattern FUNCTION = Pattern.compile(EXP_FUNCTION);
        private Pattern VARIABLE = Pattern.compile(EXP_VARIABLE);
        private String expression;
        private Stack<ExpressionNode> expNodeStack = new Stack<ExpressionNode>();
        private Map<String, Integer> supportedTypes = new HashMap<>();
    
        public ExpressionParser(String expression) {
            this.expression = expression;
            supportedTypes.put("string", Constant.STRING);
            supportedTypes.put("boolean", Constant.BOOLEAN);
            supportedTypes.put("integer", Constant.INTEGER);
            supportedTypes.put("object", Constant.OBJECT);
            supportedTypes.put("collection", Constant.COLLECTION);
        }
    
        public Stack<ExpressionNode> getExpNodeStack() {
            return expNodeStack;
        }
    
        public void parse() {
            parse(expression);
        }
    
        /**
         * 解析表达式
         *
         * @param expression
         * @return
         */
        public ExpressionNode parse(String expression) {
            if (expression == null || expression.isEmpty()) {
                return null;
            }
            ExpressionNode expNode = new ExpressionNode();
            expNodeStack.push(expNode);
            if (parseFunction(expNode, expression)) {
                return expNode;
            }
            if (parseVariable(expNode, expression)) {
                return expNode;
            }
            if (parseTemplate(expNode, expression)) {
                return expNode;
            }
            expNode.setNodeType(ExpressionNode.CONSTANT);
            expNode.setExpression(expression);
            return expNode;
        }
    
        private boolean parseFunction(ExpressionNode expNode, String expression) {
            Matcher matcher = Pattern.compile(EXP_FUNCTION).matcher(expression);
            if (!matcher.find()) {
                return false;
            }
            if (matcher.end() - matcher.start() != expression.length()) {
                return false;
            }
            expNode.setNodeType(ExpressionNode.FUNCTION);
            String fun = matcher.group("fun");
            String args = matcher.group("args");
            expNode.setExpression(fun);
            for (String argExp : args.split(COMMA)) {
                if (!argExp.isEmpty()) {
                    expNode.addArgument(parse(argExp));
                }
            }
            return true;
        }
    
        private boolean parseVariable(ExpressionNode expNode, String expression) {
            Matcher matcher = Pattern.compile(EXP_VARIABLE).matcher(expression);
            if (!matcher.find()) {
                return false;
            }
            if (matcher.end() - matcher.start() != expression.length()) {
                return false;
            }
            expNode.setNodeType(ExpressionNode.VARIABLE);
            String var = matcher.group("var");
            String type = matcher.group("type");
            String defVal = matcher.group("def");
            if (type == null) {
                type = "string"; // default type: string
            }
            Integer varType = getVarType(type);
            expNode.setExpression(var);
            expNode.setVarType(varType);
            expNode.setDefVal(defVal);
            return true;
        }
    
        /**
         *
         * @param type
         * @return
         */
        private Integer getVarType(String type) {
            if (!supportedTypes.containsKey(type)) {
                return NOT_SUPPORTED_TYPE;
            } else {
                return supportedTypes.get(type);
            }
        }
    
        /**
         * 解析(模板)表达式
         * 
         * @param expNode
         * @param expression
         * @return
         */
        private boolean parseTemplate(ExpressionNode expNode, String expression) {
            expNode.setNodeType(ExpressionNode.TEMPLATE);
            StringBuffer temp = new StringBuffer();
            int argNodeCount = processSubNode(expNode, 0, FUNCTION.matcher(expression), temp);
            if (argNodeCount > 0) {
                expression = temp.toString();
            }
            temp.setLength(0);
            argNodeCount = processSubNode(expNode, argNodeCount, VARIABLE.matcher(expression), temp);
            if (argNodeCount > 0) {
                expNode.setExpression(temp.toString());
            }
            return argNodeCount > 0;
        }
    
        /**
         * 处理子节点
         * 
         * @param expNode
         * @param argNodeCount
         * @param matcher
         * @param temp
         * @return
         */
        private int processSubNode(ExpressionNode expNode, int argNodeCount, Matcher matcher, StringBuffer temp) {
            while (matcher.find()) {
                ExpressionNode subNode = parse(matcher.group());
                if (subNode == null) {
                    continue;
                }
                expNode.addArgument(subNode);
                matcher.appendReplacement(temp, "{{" + argNodeCount + "}}");
                argNodeCount++;
            }
            matcher.appendTail(temp);
            return argNodeCount;
        }
    
    }
    View Code

    ExpressionCalculator.java -- 表达式计算器

    public class ExpressionCalculator {
        private static Logger logger = LogManager.getLogger(ExpressionCalculator.class);
        private static ConcurrentHashMap<String, Stack> expressions = new ConcurrentHashMap<>();
        private static final ExpressionCalculator calculator = new ExpressionCalculator();
    
        private ExpressionCalculator() {
        }
    
        public static Object calculate(String expression, Map<String, Object> params) {
            if (expression == null || expression.length() == 0) {
                logger.info("expression is null!");
                return null;
            }
            try {
                Stack<ExpressionNode> expNodeStack = calculator.parse(expression);
                return calculator.calculate(expNodeStack, params);
            } catch (Exception e) {
                logger.error("计算表达式异常", e);
                return null;
            }
        }
    
        public Stack<ExpressionNode> parse(String expression) {
            Stack<ExpressionNode> expNodeStack = expressions.get(expression);
            if (expNodeStack == null) {
                synchronized (ExpressionCalculator.class) {
                    if (expNodeStack == null) {
                        ExpressionParser parser = new ExpressionParser(expression);
                        parser.parse();
                        expNodeStack = parser.getExpNodeStack();
                        expressions.put(expression, expNodeStack);
                    }
                }
            }
            Stack<ExpressionNode> stackCopy = new Stack<>();
            stackCopy.addAll(expNodeStack);
            return stackCopy;
        }
    
        public Object calculate(Stack<ExpressionNode> expNodeStack, Map<String, Object> params) {
            Object result = null;
            while (!expNodeStack.isEmpty()) {
                ExpressionNode expNode = expNodeStack.pop();
                if (ExpressionNode.CONSTANT.equals(expNode.getNodeType())) {
                    expNode.setValue(expNode.getExpression());
                } else if (ExpressionNode.VARIABLE.equals(expNode.getNodeType())) {
                    Object value = getVariable(expNode, params);
                    expNode.setValue(value);
                } else if (ExpressionNode.FUNCTION.equals(expNode.getNodeType())) {
                    Object value = FunctionUtil.call(expNode.getExpression(), expNode.getArgumentValues());
                    expNode.setValue(value);
                } else if (ExpressionNode.TEMPLATE.equals(expNode.getNodeType())) {
                    String value = TemplateFormat.format(expNode.getExpression(), expNode.getArgumentValues());
                    expNode.setValue(value);
                }
                result = expNode.getValue();
            }
            return result;
        }
    
        /** 仅支持boolean/string/integer类型 **/
        private Object convert(Integer varType, String value) {
            if (value == null) {
                return null;
            }
            try {
                if (Constant.BOOLEAN.equals(varType)) {
                    return Boolean.parseBoolean(value);
                } else if (Constant.STRING.equals(varType)) {
                    return value;
                } else if (Constant.INTEGER.equals(varType)) {
                    return Integer.parseInt(value);
                } 
            } catch (Exception e) {
                logger.error("获取默认值异常", e);
            }
            return null;
        }
    
        private Object getVariable(ExpressionNode varExpNode, Map<String, Object> params) {
            Object value = JSONPath.eval(params, "$." + varExpNode.getExpression());
            if (value == null) {
                value = convert(varExpNode.getVarType(), varExpNode.getDefVal());
            }
            return value;
        }
    
        private static class TemplateFormat {
    
            public static String format(String pattern, Collection<?> collection) {
                return format(pattern, collection.toArray());
            }
    
            public static String format(String pattern, Object... objects) {
                if (objects == null || objects.length == 0) {
                    return pattern;
                }
                StringBuilder temp = new StringBuilder(pattern);
                for (int i = 0; i < objects.length; i++) {
                    String token = "{{" + i + "}}";
                    int start = temp.indexOf(token);
                    if (start == -1) {
                        break;
                    }
                    temp.replace(start, start + token.length(), String.valueOf(objects[i]));
                }
                return temp.toString();
            }
        }
        
    }
    View Code

     目前支持5中变量类型:

        /** 字符串 **/
        public static final Integer STRING = 1;
        /** 布尔值 **/
        public static final Integer BOOLEAN = 2;
        /** 整型 **/
        public static final Integer INTEGER = 3;
        /** 对象:map/pojo **/
        public static final Integer OBJECT = 4;
        /** 集合:array/list **/
        public static final Integer COLLECTION = 5;

    变量引用:

    #{var:type?def}
    ==============================================
    var -> 变量名
    type -> 类型
    def -> 当变量为空(null)时的默认值

    函数引用:

    $fun(args...)
    ===========================
    fun -> 函数名
    args -> 参数列表

    自定义函数Equals.java(参考上一篇):

    public class Equals implements IFunction {
    
        @Override
        public String getName() {
            return "equals";
        }
    
        @Override
        public String getDesc() {
            return "判断两个表达式在字面上是否相同";
        }
    
        @Override
        public Object getDefVal() {
            return false;
        }
    
        @Override
        public Object process(Object... args) throws Exception {
            if (checkArgsIsEmpty(args)) {
                return false;
            }
            Object obj1 = args[0];
            Object obj2 = args[1];
            if (obj1 == null || obj2 == null) {
                return false;
            }
            if (obj1.getClass() == obj2.getClass()) {
                return obj1.equals(obj2);
            }
            return obj1.toString().equals(obj2.toString());
        }
    
    }
    View Code

    写个例子测试下表达式计算:

        public static void main(String[] args) {
            Object user = new Object() {
                private String name = "lichmama";
    
                public String getName() {
                    return name;
                }
    
                public void setName(String name) {
                    this.name = name;
                }
            };
            String expression = "{'name': '#{user.name}', 'result': $equals(#{user.name}, lichmama})}";
            Map<String, Object> params = new HashMap<>();
            params.put("user", user);
            params.put("result", null);
            Object result = ExpressionCalculator.calculate(expression, params);
            System.out.println("模板 => " + expression);
            System.out.println("入参 => " + JSON.toJSONString(params));
            System.out.println("结果 => " + result);
        }

    输出如下:

    模板 => {'name': '#{user.name}', 'result': $equals(#{user.name}, lichmama})}
    入参 => {"user":{"name":"lichmama"}}
    结果 => {'name': 'lichmama', 'result': true}
  • 相关阅读:
    数字签名与HTTPS详解
    利用策略模式优化过多 if else 代码
    Redis 的事务到底是不是原子性的
    Spring Boot项目的接口防刷
    深入分析 ThreadLocal
    什么是四层和七层负载均衡?他们之间的区别是什么?
    MyEclipse或Eclipse中project的导入和导出
    org.hibernate.exception.ConstraintViolationException: could not insert:
    C++ STL vector(向量容器)的使用(附完整程序代码)
    Swift2.0语言教程之函数嵌套调用形式
  • 原文地址:https://www.cnblogs.com/lichmama/p/12964467.html
Copyright © 2011-2022 走看看