在目前的项目中,需要定义规则表达式,分析工单对象中的属性的值。
一、需求:
输入规则表达式,和工单对象,运算出结果。例如,规则表达式是 order.office.areaCode.equal(10000),输入参数order后,分析order里面office对象的areaCode属性的值,如果是等于10000,就返回true,否则返回false。
二、设计要求:
1、规则表达式是可以动态编辑的。
2、高扩展性,未来增加containt / begin_as 等运算符号时,较少改动量
3、可以遍历的获取参数的属性值,例如 order.office.areaCode.……
三、规则表达式的文法,与抽象语法树
1、文法
通过观察可以看到规则表达式的定义分为4部分,包括 “order”、“.office.areaCode”、“equal()”、“10000”。
“order”:输入参数。是不变的,表示输入的参数是一个order对象。
“.office.……”:获取值的操作符。表示要获取order对象里面值,而且可长可短。
“equal()”:比较操作符。表示要进行equal的比较操作。
“10000”:目标值。就是进行比较的目标值。
结果值:这个在表达式上没有显示,但是表示最终获得的结果是一个boolean值。是否符合规则。
2、抽象语法树
四、解决方案
根据以上的表达式的文法,设计了以下几个类:
DeriveRuleInput:输入参数,包括工单对象,与规则表达式
DeriveRuleOutput:输出参数,不同的操作下,返回的结果不一样。获取值的操作符,返回一个对象。比较操作符,返回的是一个boolean值。
AbstractExpression:表达式各个文法的抽象类
TargetVauleExperssion:表示目标值
OperationExpression:获取值的操作符
AbstractComparisonExpression:比较操作符的抽象类,因为以后比较的方式可能需要拓展,需要各种各样的比较方式,除了equal,还要有contain等,因此抽象出来。
CommonComparisonExpression:通用的比较操作符,匹配通用的一些比较操作符。
DeriveRuleHandler:规则计算的处理,接收工单与规则表达式,并返回最终的结果。其中的处理包括,把表达式转化成各个文房抽象类,而且运算起来。
五、具体实现:
1、输入、输出参数的定义
/**
* 拆分规则解析的输入参数
* @author hzy
*
*/
public class DeriveRuleInput {
private IWorkOrder workOrder;
private String ruleExp;
public IWorkOrder getWorkOrder() {
return workOrder;
}
public void setWorkOrder(IWorkOrder workOrder) {
this.workOrder = workOrder;
}
public String getRuleExp() {
return ruleExp;
}
public void setRuleExp(String ruleExp) {
this.ruleExp = ruleExp;
}
}
/**
* 规则分析的输出结果
* @author hzy
*
*/
public class DeriveRuleOutput {
private Boolean isValid;
private Object resultObj;
public Boolean getIsValid() {
return isValid;
}
public void setIsValid(Boolean isValid) {
this.isValid = isValid;
}
public Object getResultObj() {
return resultObj;
}
public void setResultObj(Object resultObj) {
this.resultObj = resultObj;
}
}
2、各个文法的抽象与实现
/**
* 规则表达式的抽象类
* @author hzy
*
*/
public abstract class AbstractExpression {
private String expStr;
public String getExpStr() {
return expStr;
}
public void setExpStr(String expStr) {
this.expStr = expStr;
}
/**
* 表达式的计算
* @param input
* @return
*/
public abstract DeriveRuleOutput interpret(DeriveRuleInput input);
}
/**
* 比较目标的表达式
* @author hzy
*
*/
public class TargetVauleExperssion extends AbstractExpression {
@Override
public DeriveRuleOutput interpret(DeriveRuleInput input) {
DeriveRuleOutput result = new DeriveRuleOutput();
result.setResultObj(this.getExpStr());
return result;
}
}
/**
* 获取值的操作的表达式
* @author hzy
*
*/
public class OperationExpression extends AbstractExpression {
private AbstractExpression previousExp = null;
private static final Pattern WORD_PATTERN =Pattern.compile("([a-z|A-Z])(\\w*)");
@Override
public DeriveRuleOutput interpret(DeriveRuleInput input) {
Object previousResult = null;
if(this.previousExp != null){
previousResult = this.previousExp.interpret(input).getResultObj();
}else{
//如果是第一条操作符
previousResult = input.getWorkOrder();
}
String methodOfGetOrderValue = "get" + upperFirstLetter(this.getExpStr()); //方法名
Object vauleFromOrder = null;
try {
//通过反射,调用order里面的get方法
Method method = previousResult.getClass().getMethod(methodOfGetOrderValue);
vauleFromOrder = method.invoke(previousResult);
} catch (NoSuchMethodException e) {
throw new RuntimeException("规则表达式中,属性的获取方法不存在["+methodOfGetOrderValue+"]");
} catch (Exception e) {
//统一在跑出去
throw new RuntimeException(e);
}
DeriveRuleOutput result = new DeriveRuleOutput();
result.setResultObj(vauleFromOrder);
return result;
}
/**
* 使第一个字母大写,如果首单词不是字母就返回原来的字符串,如果里面包含非法字符也返回原字符串。
* 例如 字母 abcde--->Abcde,qwe123-->Qwe123,123qwe--->123qwe,qwe%!--->qwe%!
* @param word
* @return
*/
private String upperFirstLetter(String word){
Matcher matcher = WORD_PATTERN.matcher(word);
if(!matcher.matches()){ //如果没有匹配,直接返回
return word;
}
String firstLetter = matcher.group(1).toUpperCase();
String restLetter = matcher.group(2);
return firstLetter + "" + restLetter;
}
public AbstractExpression getPreviousExp() {
return previousExp;
}
public void setPreviousExp(AbstractExpression previousExp) {
this.previousExp = previousExp;
}
}
/**
* 比较表达式 抽象类
* @author hzy
*
*/
public abstract class AbstractComparisonExpression extends AbstractExpression {
protected AbstractExpression previousExp;
protected AbstractExpression nextExp;
@Override
public DeriveRuleOutput interpret(DeriveRuleInput input) {
//上一个表达式得出的值,也就是操作符获取order的属性值
Object orderValue = this.previousExp.interpret(input).getResultObj();
//下一个表达式得出的值,就需要做比较的目标值
Object targetValue = null;
if(this.nextExp != null){
targetValue = this.nextExp.interpret(input).getResultObj();
}
boolean isValid = compareOrderToTarget(orderValue, targetValue);
DeriveRuleOutput result = new DeriveRuleOutput();
result.setIsValid(isValid);
return result;
}
/**
* 比较获取的工单的属性值,和目标的值
* @param orderValue
* @param targetValue
* @return
*/
protected abstract boolean compareOrderToTarget(Object orderValue, Object targetValue);
public AbstractExpression getPreviousExp() {
return previousExp;
}
public void setPreviousExp(AbstractExpression previousExp) {
this.previousExp = previousExp;
}
public AbstractExpression getNextExp() {
return nextExp;
}
public void setNextExp(AbstractExpression right) {
this.nextExp = right;
}
}
3、通用的比较符的操作实现
/**
* 通用字符串比较符
* @author hzy
*
*/
public class CommonComparisonExpression extends AbstractComparisonExpression {
private static Logger log = Logger.getLogger(CommonComparisonExpression.class.getName());
public static final String EXC_METHOD_EQUAL = "equal";
public static final String EXC_METHOD_NO_EQUAL = "no_equl";
public static final String EXC_METHOD_NO_EXIST = "no_exist";
public static final String EXC_METHOD_BEGIN_AS = "begin_as";
public static final String EXC_METHOD_END_AS = "end_as";
public static final String EXC_METHOD_CONTAINS = "contains";
public static final String EXC_METHOD_NO_CONTAINS = "no_cont";
public static final String EXC_METHOD_CONTAINT_KEY = "contain_key";
@Override
protected boolean compareOrderToTarget(Object orderValue, Object targetValue) {
String excMethod = this.getExpStr();
boolean isValid = false;
if(EXC_METHOD_EQUAL.equals(excMethod)){
isValid = this.excuteEqual(orderValue, targetValue);
}
if(EXC_METHOD_NO_EQUAL.equals(excMethod)){
isValid = this.excuteNoEqual(orderValue, targetValue);
}
if(EXC_METHOD_NO_EXIST.equals(excMethod)){
isValid = orderValue == null;
}
if(EXC_METHOD_CONTAINS.equals(excMethod)){
isValid = this.excuteContains(orderValue, targetValue);
}
if(EXC_METHOD_NO_CONTAINS.equals(excMethod)){
isValid = this.excuteNoContains(orderValue, targetValue);
}
if(EXC_METHOD_BEGIN_AS.equals(excMethod)){
isValid = this.excuteBeginAs(orderValue, targetValue);
}
if(EXC_METHOD_END_AS.equals(excMethod)){
isValid = this.excuteEndAs(orderValue, targetValue);
}
if(EXC_METHOD_CONTAINT_KEY.equals(excMethod)){
isValid = this.excuteContainKey(orderValue, targetValue);
}
return isValid;
}
private boolean excuteContainKey(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
if(!(orderValue instanceof Map)){
log.warning("【规则匹配】从工单获取的["+this.previousExp.getExpStr()+"]的类型是["+orderValue.getClass()+"],没法转换为Map,没法执行contain_key操作,返回false");
return false;
}
Map proValueMap = (Map) orderValue;
return proValueMap.containsKey(targetValue.toString());
}
private boolean excuteEndAs(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
if(orderValue instanceof String && targetValue != null){
return orderValue.toString().endsWith(targetValue.toString());
}else{
return false;
}
}
private boolean excuteBeginAs(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
if(orderValue instanceof String && targetValue != null){
return orderValue.toString().startsWith(targetValue.toString());
}else{
return false;
}
}
private boolean excuteNoContains(Object orderValue, Object targetValue) {
if(orderValue == null){
return true;
}
if(orderValue instanceof String && targetValue != null){
return !orderValue.toString().contains(targetValue.toString());
}else{
return false;
}
}
private boolean excuteContains(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
if(orderValue instanceof String && targetValue != null){
return orderValue.toString().contains(targetValue.toString());
}else{
return false;
}
}
private boolean excuteNoEqual(Object orderValue, Object targetValue) {
if(orderValue == null){
return true;
}
return !orderValue.toString().trim().equals(targetValue);
}
private boolean excuteEqual(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
return orderValue.toString().trim().equals(targetValue);
}
}
4、规则处理器的实现
/**
* 拆分规则分析的处理器<p/>
* 主要负责接收输入参数,并且把规则表达式转换成各个组件,并最后运行组件,解析出结果
* @author hzy
*
*/
public class DeriveRuleHandler {
private static Logger log = Logger.getLogger(DeriveRuleHandler.class.getName());
public static boolean match(IWorkOrder order, String ruleExp){
DeriveRuleInput deriveRuleInput = new DeriveRuleInput();
deriveRuleInput.setRuleExp(ruleExp);
deriveRuleInput.setWorkOrder(order);
//解析表达式
List<AbstractExpression> expressionList = parseRuleExp(ruleExp);
if(expressionList == null || expressionList.size()==0){
//TODO抛出异常
log.warning("分析表达式后,没有解析出任何规则,表达式如下:\n"+ ruleExp);
return false;
}
//生成抽象语法的树
AbstractExpression finalTree = buildExpressionTree(expressionList);
if(finalTree == null){
log.warning("分析表达式后,没有解析出任何规则,表达式如下:\n"+ ruleExp);
throw new RuntimeException("分析表达式后,没有解析出任何规则");
}
//运行计算
DeriveRuleOutput deriveRuleOutput = finalTree.interpret(deriveRuleInput);
Boolean isValid = deriveRuleOutput.getIsValid();
if(isValid == null){
throw new RuntimeException("规则匹配有异常,没有匹配结果,规则如下:\n" + ruleExp);
}
return isValid;
}
/**
* 组织各个表达式的运行顺序,并生成语法树
* @param expressionList
* @return
*/
public static AbstractExpression buildExpressionTree(List<AbstractExpression> expressionList){
//声明一个栈对象用于存储抽象语法树
Stack<AbstractExpression> stack = new Stack<AbstractExpression>();
for(int i=0; i<expressionList.size(); i++){
AbstractExpression previous = null;
if(i != 0){
//把前一次的操作拿出来
previous = stack.pop();
}
if(expressionList.get(i) instanceof OperationExpression){
//如果是获取值的操作符,就要先把前一次的操作拿出来,并设为上一步。
OperationExpression operateExp = (OperationExpression) expressionList.get(i);
operateExp.setPreviousExp(previous);
stack.push(operateExp);
}
if(expressionList.get(i) instanceof AbstractComparisonExpression){
//如果是比较值的操作符,就要把前一次的操作拿出来,已经后一次的操作拿出来。然后存到一起做比较
AbstractComparisonExpression comparisonExp = (AbstractComparisonExpression) expressionList.get(i);
comparisonExp.setPreviousExp(previous);
if(i+1 <expressionList.size()){ //如果存在下一个操作的表达式
AbstractExpression next = expressionList.get(i + 1);
if(next instanceof TargetVauleExperssion){
//而且下一个表示式是目标值的表达式
comparisonExp.setNextExp(next);
}
}
stack.push(comparisonExp);
}
if(expressionList.get(i) instanceof TargetVauleExperssion){
stack.push(previous);
}
}
AbstractExpression finalTree = stack.pop();
return finalTree;
}
/**
* 解析规则表达式,把表达式的各个部分,解析成对应的Expression
* @param ruleExp
* @return
*/
public static List<AbstractExpression> parseRuleExp(String ruleExp){
List<AbstractExpression> expressionList = new ArrayList<AbstractExpression>();
int startIndex= 0;
for(int endIndex=0; endIndex<ruleExp.length(); endIndex++){
if(ruleExp.charAt(endIndex) == '.'
|| ruleExp.charAt(endIndex) == '('
|| ruleExp.charAt(endIndex) == ')'
|| endIndex == ruleExp.length()-1){
//把每个两个符号之间的字符提取出来,并做解析
//例如,order.office.areaCode.equal(10000)
//截取出 .office. 然后解析出OpreationExpression
String tmp = ruleExp.substring(startIndex, endIndex+1);
AbstractExpression expression = matchAndCreateExpression(tmp);
if(expression == null){
continue;
}
expressionList.add(expression);
startIndex = endIndex;
}
}
return expressionList;
}
/**
* 根据表达式的各个字符串,生成Expression
* @param ruleExp
* @return
*/
private static AbstractExpression matchAndCreateExpression(String ruleExp) {
Matcher matcher = null;
//匹配 获取指令的操作符,如果符合 ".单词.",单词表示通配
Pattern operationExpressionP = Pattern.compile(".*\\.(\\w+)\\.");
matcher = operationExpressionP.matcher(ruleExp);
if(matcher.find()){
AbstractExpression expresson = new OperationExpression();
expresson.setExpStr(matcher.group(1));
return expresson;
}
//匹配比较值的操作符,如果符合 ".单词(",单词表示通配
Pattern comparisonExpressionP = Pattern.compile(".*\\.(\\w+)\\(");
matcher = comparisonExpressionP.matcher(ruleExp);
if(matcher.find()){
AbstractExpression expresson = new CommonComparisonExpression();
expresson.setExpStr(matcher.group(1));
return expresson;
}
//匹配目标值的操作符,如果符合 "(单词)",单词表示通配
Pattern targetExpressionP = Pattern.compile("\\(\\s*(.+)\\s*\\)");
matcher = targetExpressionP.matcher(ruleExp);
if(matcher.find()){
AbstractExpression expresson = new TargetVauleExperssion();
expresson.setExpStr(matcher.group(1));
return expresson;
}
//留着待扩展
//都不符合返回空
return null;
}
}
六、关于修改与拓展
如果以后在应用的过程中,发现匹配错误或失败的情况,只需要找出匹配失败的操作符,并修改改操作符的实现类就可以了。不需要影响系统的其他部分。如果需要添加新的操作符,也是只用实现AbstractExpression就可以了。
举个例,现在来了新的需求,需要添加新的比较方式,对于对象里面Map值,查找里面有没有包含 特定Key,把比较符定义为 contain_key()。
例如,order 里面有一个属性 itemMap,是一个Map,里面存很多参数,如果里面包含一个 Key 为 abc的值,那么就符合规则,返回true。规则为 order.itemMap.contain_key(abc)
由于我已经定义了一个比较符的抽象类 AbstractComparisonExpression,所以直接继承实现就可以。另外,需要处理能识别contain_key 这个语法,所以还要修改处理器DeriveRuleHandler的识别部分,具体代码如下:
/**
* contain_key的比较操作符的执行
* @author hzy
*
*/
public class ContainKeyComparisonExpression extends AbstractComparisonExpression {
private static Logger log = Logger.getLogger(ContainKeyComparisonExpression.class.getName());
@Override
protected boolean compareOrderToTarget(Object orderValue, Object targetValue) {
if(orderValue == null){
return false;
}
if(!(orderValue instanceof Map)){
log.warning("【规则匹配】从工单获取的["+this.previousExp.getExpStr()+"]的类型是["+orderValue.getClass()+"],没法转换为Map,没法执行contain_key操作,返回false");
return false;
}
Map proValueMap = (Map) orderValue;
return proValueMap.containsKey(targetValue.toString());
}
}
处理识别部分的修改
//匹配contain_key比较操作符,如果符合 ".contain_key("
//add by hzy 2014-12-10
Pattern containtKeyExpression = Pattern.compile("\\.(contain_key)\\(");
matcher = containtKeyExpression.matcher(ruleExp);
if (matcher.find()) {
AbstractExpression expresson = new ContainKeyComparisonExpression();
expresson.setExpStr(matcher.group(1));
return expresson;
}
七、后续
突然发现,这里有问题了,添加新的操作符,是不应该还改其他地方的,如果还有其他改动,说明这里还有问题。最为完善的方式是,添加新的操作符,只需要添加类就行了,不应该到其他地方修改,所以这里就要考虑重构了。