如何使用" varchars,自定义函数,数字和日期执行表达式(eval)"

时间:2014-08-06 07:05:18

标签: java eval formula varchar

评估包含操作,变量,数字,自定义函数,日期的自定义表达式的正确方法是什么。

一开始我正在使用'Udo Klimaschewski'Expression utility

一切都很好,但是这个实用程序不能使用varchars,只能使用数字。 但是需要执行的公式也可以包含varchars,preview:=((1 = 2)和('abc'='abc'))

对于约会,我做了一件棘手的事情,将日期表达式转换为long然后进行比较。 修改实用程序的示例代码:

的eval()

    public BigDecimal eval() {

    Stack<BigDecimal> stack = new Stack<BigDecimal>();

    for (String token : getRPN()) {
        mylog.pl("Reverse polish notation TOKEN : " + token + " RPN size: " + getRPN().size() );
        if (operators.containsKey(token)) {
            BigDecimal v1 = stack.pop();
            BigDecimal v2 = stack.pop();
            stack.push(operators.get(token).eval(v2, v1));
        } else if (variables.containsKey(token)) {
            stack.push(variables.get(token).round(mc));
        } else if (functions.containsKey(token.toUpperCase())) {
            Function f = functions.get(token.toUpperCase());
            ArrayList<BigDecimal> p = new ArrayList<BigDecimal>(f.getNumParams());
            for (int i = 0; i < f.numParams; i++) {
                p.add(0, stack.pop());
            }
            BigDecimal fResult = f.eval(p);
            stack.push(fResult);
        } else if (isDate(token)) {
            Long date = null;
            try {
                date = SU.sdf.parse(token).getTime();
            } catch (ParseException e) {/* IGNORE! */
            }
            stack.push(new BigDecimal(date, mc));
        } else {
//Here now is error because expresion is varchar not a number 
//java.lang.NumberFormatException
                stack.push(new BigDecimal(token, mc));
            }
        }
        return stack.pop().stripTrailingZeros();
    }

反向表示法

    private List<String> getRPN() {
    if (rpn == null) {
        rpn = shuntingYard(this.expression);
    }
    return rpn;
}

ShuntingYard

private List<String> shuntingYard(String expression) {
    List<String> outputQueue = new ArrayList<String>();
    Stack<String> stack = new Stack<String>();

    Tokenizer tokenizer = new Tokenizer(expression);

    String lastFunction = null;
    while (tokenizer.hasNext()) {
        String token = tokenizer.next();
        if (isNumber(token)) {
            outputQueue.add(token);                     
        } else if (variables.containsKey(token)) {
            outputQueue.add(token);
        } else if (functions.containsKey(token.toUpperCase())) {
            stack.push(token);
            lastFunction = token;
        } else if (Character.isLetter(token.charAt(0))) {
            if (BusinessStrategy.PREFIX_X.equals(Character.toString(token.charAt(0)))){
                //HERE can catch all varchar's prefix is added before this utility
                outputQueue.add(token);                    
            } else {
                stack.push(token);                    
            }
        } else if (",".equals(token)) {
            while (!stack.isEmpty() && !"(".equals(stack.peek())) {
                outputQueue.add(stack.pop());
            }
            if (stack.isEmpty()) {
                throw new ExpressionException("Parse error for function '"
                        + lastFunction + "'");
            }
        } else if (operators.containsKey(token)) {
            Operator o1 = operators.get(token);
            String token2 = stack.isEmpty() ? null : stack.peek();
            while (operators.containsKey(token2)
                    && ((o1.isLeftAssoc() && o1.getPrecedence() <= operators
                            .get(token2).getPrecedence()) || (o1
                            .getPrecedence() < operators.get(token2)
                            .getPrecedence()))) {
                outputQueue.add(stack.pop());
                token2 = stack.isEmpty() ? null : stack.peek();
            }
            stack.push(token);
        } else if ("(".equals(token)) {
            stack.push(token);
        } else if (")".equals(token)) {
            while (!stack.isEmpty() && !"(".equals(stack.peek())) {
                outputQueue.add(stack.pop());
            }
            if (stack.isEmpty()) {
                throw new RuntimeException("Mismatched parentheses");
            }
            stack.pop();
            if (!stack.isEmpty()
                    && functions.containsKey(stack.peek().toUpperCase())) {
                outputQueue.add(stack.pop());
            }
        }
    }
    while (!stack.isEmpty()) {
        String element = stack.pop();
        if ("(".equals(element) || ")".equals(element)) {
            throw new RuntimeException("Mismatched parentheses");
        }
        if (!operators.containsKey(element)) {
            throw new RuntimeException("Unknown operator or function: "
                    + element);
        }
        outputQueue.add(element);
    }
    return outputQueue;
}

也许有人已经做过类似的事情,可以帮忙提出建议吗? 谢谢!

EDIT1 我现在编辑eval现在我不知道如何处理自定义函数:(

public Object eval() {

    Stack<Object> stack = new Stack<Object>();

    for (String token : getRPN()) {
        mylog.pl("Reverse polish notation TOKEN : " + token + " RPN size: " + getRPN().size() );
        if (operators.containsKey(token)) {
            Object v1 = stack.pop();
            Object v2 = stack.pop();
            stack.push(operators.get(token).eval(v2, v1));
        } else if (variables.containsKey(token)) {
            stack.push(variables.get(token).round(mc));
        } else if (functions.containsKey(token.toUpperCase())) {
           /* Function f = functions.get(token.toUpperCase());
            ArrayList<BigDecimal> p = new ArrayList<BigDecimal>(f.getNumParams());
            for (int i = 0; i < f.numParams; i++) {
                p.add(0, stack.pop());
            }
            BigDecimal fResult = f.eval(p);
            stack.push(fResult);*/
        } else if (isDate(token)) {
            Long date = null;
            try {
                date = SU.sdf.parse(token).getTime();
            } catch (ParseException e) {/* IGNORE! */
            }
            stack.push(new BigDecimal(date, mc));
        } else {
            //stack.push(new BigDecimal(token, mc));
            if (BusinessStrategy.PREFIX_X.equals(Character.toString(token.charAt(0)))) {
                //Push the string without quotes.
                mylog.pl("VARCHAR PUSH");
                stack.push(token.substring(0, token.length()));
            } else {
                stack.push(new BigDecimal(token, mc));
            }
        }
    }
    return stack.pop();//.stripTrailingZeros();
}

也许是如何规范化这段代码的?

       addOperator(new Operator("=", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            if (v1.getClass() == v2.getClass()){
               if (v1 instanceof String){
                   String s1 = (String) v1;
                   String s2 = (String) v2;
                   return s1.equals(s2) == true ? BigDecimal.ONE : BigDecimal.ZERO;
               } else if (v1 instanceof BigDecimal){
                   BigDecimal b1 = (BigDecimal) v1;
                   BigDecimal b2 = (BigDecimal) v2;
                   return b1.compareTo(b2) == 0 ? BigDecimal.ONE : BigDecimal.ZERO;
               }
            } else {
                // TODO Throw something
                mylog.pl("Wrong types");
                return null;
            }
            //How to avoid this return ? 
            return null;
        }
    });
    addOperator(new Operator("==", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            return operators.get("=").eval(v1, v2);
        }
    });

    addOperator(new Operator("!=", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            if (v1.getClass() == v2.getClass()){
                if (v1 instanceof String){
                    String s1 = (String) v1;
                    String s2 = (String) v2;
                    return s1.equals(s2) == false ? BigDecimal.ONE : BigDecimal.ZERO;
                } else if (v1 instanceof BigDecimal){
                    BigDecimal b1 = (BigDecimal) v1;
                    BigDecimal b2 = (BigDecimal) v2;
                    return b1.compareTo(b2) != 0 ? BigDecimal.ONE : BigDecimal.ZERO;
                }
             } else {
                 // TODO Throw something
                 mylog.pl("Wrong types");
                 return null;
             }
             //How to avoid this return ? 
             return null;
        }
    });
    addOperator(new Operator("<>", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            return operators.get("!=").eval(v1, v2);
        }
    });

如何处理这些运营商? 对于varchars只需要等于或不等于如何轻松并通常重写这些?

    addOperator(new Operator("<=", 10, false) {
        @Override
        public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
            return v1.compareTo(v2) <= 0 ? BigDecimal.ONE : BigDecimal.ZERO;
        }
    });

        addOperator(new Operator("and", 4, false) {
        @Override
        public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
            boolean b1 = !v1.equals(BigDecimal.ZERO);
            boolean b2 = !v2.equals(BigDecimal.ZERO);
            return b1 && b2 ? BigDecimal.ONE : BigDecimal.ZERO;
        }
    });

自定义功能

        addFunction(new Function("SalStrLeftX", 2) {
        @Override
        public BigDecimal eval(List<BigDecimal> parameters) {
            Object o = null;
            try {
                Class<?> c = Class.forName("com.disnet.business.Functions");
                Method  method = c.getDeclaredMethod ("SalStrLeftX", String.class, int.class);
                o = method.invoke (c.newInstance(), parameters.get(0).toPlainString(), parameters.get(1).intValueExact());
                System.out.println("Value from SalStrLeftX "+o);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //TODO for Varchar
            return SU.getBigDecimal(o);
        }
    });

1 个答案:

答案 0 :(得分:1)

首先,您需要多种类型。你可以引入一个新的类Value,但是Object会这样做。

public Object eval() {

Stack<Object> stack = new Stack<>();


    } else {
        if (token.matches("(?s)'.*'") {
            stack.push(token.substring(1, token.length() - 1);
        } else {
            stack.push(new BigDecimal(token, mc));
        }
    }

不带引号推送字符串。

然后运营商也必须处理String。最简单的是进行运行时转换:

在伪代码中:

Object rhs = stack.pop();
Object lhs = stack.pop();
TypeCasted casted = operator.typeCast(lhs, rhs);
lhs = casted.lhs;
rhs = casted.rhs;
stack.push(operator.eval(lhs, rhs));

最简单的类型转换,即=将是:

if (lhs.getClass() != rhs.getClass() {
    if (lhs instanceof String) {
        rhs = String.valueOf(rhs);
    }
    ...
}
return new TypeCasted(lhs, rhs);

编辑问题后:

        if (v1.getClass() == v2.getClass()){
           if (v1 instanceof Comparable){
               Comparable b1 = (Comparable) v1;
               Comparable b2 = (Comparable) v2;
               return b1.compareTo(b2) == 0 ? BigDecimal.ONE : BigDecimal.ZERO;
           } else {
               throw new IllegalStateException("Comparable expected instead of: "
                   + v1.getClass().getName());
           }
       } else {

顺便说一下,您可能会引入许多类型:BigDecimal,Integer等,allimplement Number,可以用作((Number)obj).longValue()