Ver código fonte

采用内置的算术表达式计算器代jexl来实现算术计算,同时不再依赖jexl包

jacky6024 8 anos atrás
pai
commit
a7b9e6e14f

+ 0 - 5
urule-core/pom.xml

@@ -48,11 +48,6 @@
 			</exclusion>
 		</exclusions>
 	</dependency>
-	<dependency>
-		<groupId>org.apache.commons</groupId>
-		<artifactId>commons-jexl</artifactId>
-		<version>2.1.1</version>
-	</dependency>
 	<dependency>
 		<groupId>org.codehaus.jackson</groupId>
 		<artifactId>jackson-mapper-asl</artifactId>

+ 220 - 0
urule-core/src/main/java/com/bstek/urule/runtime/ElUtils.java

@@ -0,0 +1,220 @@
+package com.bstek.urule.runtime;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Stack;
+
+import com.bstek.urule.Utils;
+
+/**
+ * @author Jacky.gao
+ * @since 2017年10月27日
+ */
+public class ElUtils {
+    private static int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 运用运算符ASCII码-40做索引的运算符优先级
+    
+    public static void main(String[] args) {
+    	try{
+    		String expr="'gaojie:'+1.3243248E7+1024*1024";
+    		System.out.println(eval(expr));    		
+    	}catch(Exception ex){
+    		System.out.println("="+ex.getMessage()+"=");
+    	}
+	}
+
+    public static Object eval(String expression) {
+    	expression = transform(expression);
+    	Object result = calculate(expression);
+        return result;
+    }
+
+    /**
+     * 将表达式中负数的符号更改
+     * @param expression 例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1)
+     * @return 返回转换结果
+     */
+    private static String transform(String expression) {
+        char[] arr = expression.toCharArray();
+        for (int i = 0; i < arr.length; i++) {
+        	char cc=arr[i];
+            if (cc == '-') {
+                if (i == 0) {
+                    arr[i] = '~';
+                } else {
+                    char c = arr[i - 1];
+                    if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {
+                        arr[i] = '~';
+                    }
+                }
+            }
+        }
+        if(arr[0]=='~'||arr[1]=='('){
+            arr[0]='-';
+            return "0"+new String(arr);
+        }else{
+            return new String(arr);
+        }
+    }
+
+    /**
+     * 按照给定的表达式计算
+     * @param expression 要计算的表达式例如:5+12*(3+5)/7
+     * @return 返回计算结果
+     */
+    private static Object calculate(String expression) {
+    	Stack<String> postfixStack = new Stack<String>();
+        Stack<Object> resultStack = new Stack<Object>();
+        prepare(expression,postfixStack);
+        Collections.reverse(postfixStack);// 将后缀式栈反转
+        while (!postfixStack.isEmpty()) {
+            String currentValue = postfixStack.pop();
+            if (currentValue.equals("") || !isOperator(currentValue.charAt(0))) {// 如果不是运算符则存入操作数栈中
+            	if(currentValue.startsWith("\"")){
+            		currentValue=currentValue.substring(1,currentValue.length()-1);
+            	}
+                currentValue = currentValue.replace("~", "-");
+                resultStack.push(currentValue);
+            } else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
+                String secondValue = resultStack.pop().toString();
+                String firstValue = resultStack.pop().toString();
+
+                // 将负数标记符改为负号
+                firstValue = firstValue.replace("~", "-");
+                secondValue = secondValue.replace("~", "-");
+
+                Object tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
+                resultStack.push(tempResult);
+            }
+        }
+        return resultStack.pop();
+    }
+
+    private static void prepare(String expression,Stack<String> postfixStack) {
+    	Stack<Character> opStack = new Stack<Character>();
+        opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低
+        char[] arr = expression.toCharArray();
+        int currentIndex = 0;// 当前字符的位置
+        int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
+        char currentOp, peekOp;// 当前操作符和栈顶操作符
+        String prevData=null;
+        for (int i = 0; i < arr.length; i++) {
+            currentOp = arr[i];
+            if(isInvertedComma(currentOp)){
+            	currentIndex=i+1;
+            	i++;
+            	count=1;
+            	while(i<arr.length){
+            		currentOp = arr[i];
+            		if(isInvertedComma(currentOp)){
+                		prevData=new String(arr, currentIndex, count-1);
+                		prevData="\""+prevData+"\"";
+                		break;
+            		}
+            		i++;
+            		count++;
+            	}
+            	continue;
+            }else if(isOperator(currentOp)) {
+            	//当前字符是运算符
+            	if(prevData!=null){
+            		postfixStack.push(prevData);
+            		prevData=null;
+            	}else if (count > 0) {
+            		String data=new String(arr, currentIndex, count).trim();
+                    postfixStack.push(data);
+                    //取两个运算符之间的内容
+                }
+                peekOp = opStack.peek();
+                if (currentOp == ')') {
+                	//遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
+                    while (opStack.peek() != '(') {
+                        postfixStack.push(String.valueOf(opStack.pop()));
+                    }
+                    opStack.pop();
+                } else {
+                    while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
+                        postfixStack.push(String.valueOf(opStack.pop()));
+                        peekOp = opStack.peek();
+                    }
+                    opStack.push(currentOp);
+                }
+                count = 0;
+                currentIndex = i + 1;
+            } else {
+                count++;
+            }
+        }
+        if(prevData!=null){
+        	postfixStack.push(prevData);	
+        }else if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
+            postfixStack.push(new String(arr, currentIndex, count));
+        }
+
+        while (opStack.peek() != ',') {
+            postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
+        }
+    }
+    
+    private static boolean isInvertedComma(char c){
+    	return c=='"';
+    }
+    
+    private static boolean isOperator(char c) {
+        return c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '(' || c == ')';
+    }
+    public static boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
+    	if(cur=='%')cur='*';
+    	if(peek=='%')peek='*';
+        boolean result = false;
+        if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
+            result = true;
+        }
+        return result;
+    }
+
+    private static Object calculate(String firstValue, String secondValue, char currentOp) {
+        Object result = null;
+        BigDecimal first,second;
+        try{
+        	switch (currentOp) {
+        	case '+':
+        		first=Utils.toBigDecimal(firstValue);
+        		second=Utils.toBigDecimal(secondValue);
+        		result=first.add(second);
+        		break;
+        	case '-':
+        		first=Utils.toBigDecimal(firstValue);
+        		second=Utils.toBigDecimal(secondValue);
+        		result=first.subtract(second);
+        		break;
+        	case '*':
+        		first=Utils.toBigDecimal(firstValue);
+        		second=Utils.toBigDecimal(secondValue);
+        		result=first.multiply(second);
+        		break;
+        	case '/':
+        		first=Utils.toBigDecimal(firstValue);
+        		second=Utils.toBigDecimal(secondValue);
+        		result=first.divide(second);
+        		break;
+        	case '%':
+        		first=Utils.toBigDecimal(firstValue);
+        		second=Utils.toBigDecimal(secondValue);
+        		result=first.doubleValue() % second.doubleValue();
+        		break;
+        	}
+        }catch(Exception ex){
+        	String msg=ex.getMessage() == null ? "" : ex.getMessage();
+        	String a=(firstValue == null ? "null" : firstValue.toString());
+        	String b=(secondValue == null ? "null" : secondValue.toString());
+			if(currentOp=='+'){
+				result = a+b;				
+			}else if(currentOp=='/' && msg.indexOf("by zero")!=-1){
+				result = "Infinity";
+			}else{
+				throw ex;							
+			}
+        }
+        return result;
+    }
+}

+ 2 - 6
urule-core/src/main/java/com/bstek/urule/runtime/rete/ContextImpl.java

@@ -19,11 +19,10 @@ package com.bstek.urule.runtime.rete;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.commons.jexl2.Expression;
-import org.apache.commons.jexl2.JexlEngine;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.context.ApplicationContext;
 
+import com.bstek.urule.runtime.ElUtils;
 import com.bstek.urule.runtime.WorkingMemory;
 import com.bstek.urule.runtime.assertor.AssertorEvaluator;
 
@@ -37,14 +36,12 @@ public class ContextImpl implements Context {
 	private Map<String,String> variableCategoryMap;
 	private ValueCompute valueCompute;
 	private WorkingMemory workingMemory;
-	private JexlEngine jexlEngine;
 	public ContextImpl(WorkingMemory workingMemory,ApplicationContext applicationContext,Map<String,String> variableCategoryMap) {
 		this.workingMemory=workingMemory;
 		this.applicationContext = applicationContext;
 		this.assertorEvaluator=(AssertorEvaluator)applicationContext.getBean(AssertorEvaluator.BEAN_ID);
 		this.variableCategoryMap=variableCategoryMap;
 		this.valueCompute=(ValueCompute)applicationContext.getBean(ValueCompute.BEAN_ID);
-		this.jexlEngine=new JexlEngine();
 	}
 	@Override
 	public WorkingMemory getWorkingMemory() {
@@ -60,8 +57,7 @@ public class ContextImpl implements Context {
 	
 	@Override
 	public Object parseExpression(String expression) {
-		Expression expr=jexlEngine.createExpression(expression);
-		return expr.evaluate(null);
+		return ElUtils.eval(expression);
 	}
 	
 	public String getVariableCategoryClass(String variableCategory) {