评估包含操作,变量,数字,自定义函数,日期的自定义表达式的正确方法是什么。
一开始我正在使用'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);
}
});
答案 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()
。