java 实现逻辑表达式解析

java 实现逻辑表达式解析

老黄瓜001

于 2020-07-28 17:16:30 发布

这里的字符串分割使用的默认空格分隔,使用时需要特别注意模板格式。

废话不多说,直接上代码

package com.lunua.parse;
 
import com.alibaba.fastjson.JSONPath;
import com.google.common.collect.Lists;
 
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
 
public class Parser {
    enum Op{
        GT(">"),GE(">="),LT("<"),LE("<="),EQ("=="),AND("and"),OR("or"),CONTAINS("contains"),CONTAINS_NOT("containsnot");
        private String operator;
 
        Op(String operator) {
            this.operator = operator;
        }
 
        public String getOperator() {
            return operator;
        }
        public static Op getByOperator(String operator){
            if (operator == null || operator.length() == 0){
                return null;
            }
            for (Op op: Op.values()) {
                if (op.getOperator().equalsIgnoreCase(operator)){
                    return op;
                }
            }
            return null;
        }
    }
 
 
 
    private static final List<String> operator = Lists.newArrayList(Op.GT.getOperator(), Op.GE.getOperator(), Op.LT.getOperator(),Op.LE.getOperator(),Op.EQ.getOperator(),Op.AND.getOperator(),Op.OR.getOperator(),Op.CONTAINS.getOperator(),Op.CONTAINS_NOT.getOperator());
    private static Map<String, Integer> operatorMap = null;
    static {
        operatorMap = new HashMap<String, Integer>();
        operatorMap.put("(", 1);
        operatorMap.put(")", 1);
        operatorMap.put("and", 11);
        operatorMap.put("or", 12);
        operatorMap.put("==", 7);
        operatorMap.put("!=", 7);
        operatorMap.put(">", 6);
        operatorMap.put(">=", 6);
        operatorMap.put("<", 6);
        operatorMap.put("<=", 6);
        operatorMap.put("contains",14);
        //operatorMap.put("containsNot",14);
        operatorMap.put("containsnot",14);
    }
 
    private ArrayList<String> getTokens(String pattern){
        String[] splitArr = pattern.split(" ");
        ArrayList<String> tokens = new ArrayList<String>();
        for (String split:splitArr) {
            String trim = split.trim();
            if (trim != null && trim.length() != 0){
                tokens.add(trim);
            }
        }
        return tokens;
    }
    /**
     * 计算表达式
     * @return
     * @throws Exception
     */
    public boolean calculate(String pattern,String log) throws Exception {
        Stack<String> stack = new Stack<String>();
        ArrayList<String> tokens = generateReversePolish(getTokens(pattern));
        for (String token :tokens) {
            //如果是操作数则入栈
            if (!operatorMap.containsKey(token.toLowerCase())){
                stack.push(token);
            }
            else{
                String right = getValueFromContext(stack.pop(),log);
                String left = getValueFromContext(stack.pop(), log);
                stack.push(operate(left, right, token).toString());
            }
        }
        if (stack.size() == 1){
            String valueString = stack.pop();
            if (valueString.equalsIgnoreCase("true")){
                return true;
            }
        }
        return false;
    }
 
    private String getValueFromContext(String variable, String log) {
        if (!variable.contains("$")){
            return variable;
        }else {
            if (variable.equalsIgnoreCase("$")){
                return log;
            }else if (variable.contains("$.")){
                //使用jsonpath解析
                return  String.valueOf(JSONPath.read(log, variable));
            }
        }
        return null;
    }
 
 
    private ArrayList<String> generateReversePolish(ArrayList<String> tokens) throws Exception{
        //运算符stack, 和 操作数stack
        Stack<String> operatorStack = new Stack<String>();
        Stack<String> numberStack = new Stack<String>();
        for (String token : tokens) {
            //如果是操作数,直接插入到操作数stack
            if (!operatorMap.containsKey(token.toLowerCase())){
                numberStack.push(token);
            }
            else{
                if (operatorStack.empty()){
                    operatorStack.push(token);
                }
                else{
                    //如果是右括号 则要找到左括号 并且一次入栈到 操作数栈
                    if (token.equals(")")){
                        String popToken = null;
                        try {
                            while( !(popToken = operatorStack.pop()).equals("(") ){
                                numberStack.push(popToken);
                            }
                            continue;
                        } catch (EmptyStackException e) {
                            throw new Exception("invalid expression:  '", e);
                        }
                    }
                    String preOperator = operatorStack.peek();
                    //如果之前的操作符是( ,则不用比较优先级 当前操作符直接入栈
                    if (preOperator.equals("(")){
                        operatorStack.push(token);
                    }
                    //比较操作符优先级, 如果该操作符优先级大于等于 , 则直接入栈
                    else if (operatorMap.get(token) <= operatorMap.get(preOperator)){
                        operatorStack.push(token);
                    }
                    //如果该操作符优先级 小于, 则在将该操作符如栈之前 要把栈顶操作符 弹出 插入 操作数栈
                    else{
                        numberStack.push( operatorStack.pop());
                        operatorStack.push(token);
                    }
 
                }
 
            }
        }
        while (!operatorStack.empty()) {
            numberStack.push( operatorStack.pop());
        }
        ArrayList<String> resArrayList = new ArrayList<String>();
        String[] array = numberStack.toArray(new String[]{});
        for (int i = 0; i < array.length; i++) {
            resArrayList.add(array[i]);
        }
        return resArrayList;
    }
    private Boolean operate(String left,String right,String op){
        Op operator = Op.getByOperator(op);
        if (operator == null){
            System.out.println("未知的操作 op = "+op);
            return false;
        }
        Double leftNum =null;
        Double rightNum = null;
        switch (operator) {
            case GT:
                 leftNum = Double.valueOf(left);
                 rightNum = Double.valueOf(right);
                return leftNum>rightNum;
            case GE:
                 leftNum = Double.valueOf(left);
                 rightNum = Double.valueOf(right);
                return leftNum>=rightNum;
            case LT:
                 leftNum = Double.valueOf(left);
                 rightNum = Double.valueOf(right);
                return leftNum<rightNum;
            case LE:
                 leftNum = Double.valueOf(left);
                 rightNum = Double.valueOf(right);
                return leftNum<=rightNum;
            case EQ:
                return left.equals(right);
            case AND:
                if (!left.equals("true")){
                    return false;
                }
                if (!right.equals("true")){
                    return false;
                }
                return true;
            case OR:
                if (left.equals("true")){
                    return true;
                }
                if (right.equals("true")){
                    return true;
                }
                return false;
            case CONTAINS:
                if (left==null || right==null){
                    return false;
                }
                return left.contains(right);
            case CONTAINS_NOT:
                if (left==null || right==null){
                    return false;
                }
                return !left.contains(right);
            default:return false;
        }
    }
 
    public static void main(String[] args) throws Exception {
        Parser parser = new Parser();
        //System.out.println(parser.calculate("$ contains zhangsan","name=zhangsan"));
        //System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age > 12 )","{\"name\":\"zhangsan\",\"age\":14}"));
        System.out.println(parser.calculate("( $.name CONTAINSNOT zhangsan ) and ( $.age > 12 )","{\"name\":\"zhangsaan1\",\"age\":14}"));
        System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age >= 12 )","{\"name\":\"zhangsan\",\"age\":12}"));
        System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age >= 12 )","{\"name\":\"zhangsan\",\"age\":11}"));
    }
}
 
 

 



参考:
https://juejin.im/post/5b9bb590e51d450e7579cf0d
https://gitee.com/wangzhuoa/ExpressionParser
pom依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.4.0</version>
</dependency>
 
这里json解析使用到了jsonpath

 


————————————————
版权声明:本文为CSDN博主「老黄瓜001」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33894426/article/details/107640591

posted @ 2022-05-07 10:28  1156740846  阅读(332)  评论(0编辑  收藏  举报