评估可以包含操作、varchars、数字、自定义函数、日期的自定义表达式的正确方法是什么。
一开始我使用“Udo Klimaschewski”Expression utility
一切都很好,但该实用程序不适用于 varchars,仅适用于数字。 但需要执行的公式也可以包含varchar,预览: =((1=2) and ('abc'= 'abc'))
对于日期,我做了一件棘手的事情,将日期表达式转换为长整型,然后进行比较。 修改后的实用程序的示例代码:
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;
}
调车场
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;
}
也许有人已经做过类似的事情并且可以提供建议? 谢谢!
编辑1 我像这样编辑了 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);
}
});
最佳答案
首先您需要不止一种类型。您可以引入一个新类 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));
}
}
推送不带引号的字符串。
然后运算符也必须处理字符串。最简单的是进行运行时转换:
在伪代码中:
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等,所有实现Number,都可以用作((Number)obj).longValue()
.
关于java - 如何使用 "varchars,custom functions,numbers and dates"执行(eval)表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25154197/