写点什么

案例研究之聊聊 QLExpress 源码 (七)

发布于: 2021 年 01 月 15 日
案例研究之聊聊 QLExpress 源码 (七)

规则拆解为行为,条件(节点,类型),管理器,结果, 如果我们要实现规则的话可以借鉴其拆解结构,进行节点规则管理。

七、规则模块(rule)

7.1、Action(动作行为)


package com.ql.util.express.rule;
/** * 节点的动作 * Created by tianqiao on 16/12/12. */public class Action extends Node{
public Action(String text) { this.setText(text); }}

复制代码

7.2、Condition(节点条件)

package com.ql.util.express.rule;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;import java.util.List;
/** * 节点条件 * Created by tianqiao on 16/12/6. */
public class Condition extends Node{
/** * 是否优先 */ private boolean prior = false; /** * 条件类型 */ private ConditionType type; /** * 子条件集合 */ private List<Condition> children;
public Condition(ConditionType type) { this.type = type; }
public Condition() { }
public boolean isPrior() { return prior; }
public void setPrior(boolean prior) { this.prior = prior; }
public ConditionType getType() { return type; }
public void setType(ConditionType type) { this.type = type; }
public List<Condition> getChildren() { return children; }
public void setChildren(List<Condition> children) { this.children = children; }
public void addChild(Condition child) { if(this.children==null){ this.children = new ArrayList<Condition>(); } this.children.add(child); }
@Override public String toString() { if(this.type == ConditionType.Leaf){ return priorString(this.getText()); } else if(this.type == ConditionType.And){ return priorString(StringUtils.join(this.getChildren()," and ")); } else if(this.type == ConditionType.Or){ return priorString(StringUtils.join(this.getChildren()," or ")); } return null; }
private String priorString(String orig) { if(this.isPrior()){ return new StringBuilder("(").append(orig).append(")").toString(); } return orig;
}}

复制代码


7.3、ConditionType(条件类型)

package com.ql.util.express.rule;
/** * 条件类型 * Created by tianqiao on 16/12/6. */public enum ConditionType {
And, Or, Leaf}

复制代码

7.4、Node(节点)

package com.ql.util.express.rule;
/** * 节点 * Created by tianqiao on 16/12/12. */public class Node { /** * 节点ID */ private Integer nodeId; /** * 级别 */ private Integer level; /** * 文本信息 */ private String text;
public Integer getNodeId() { return nodeId; } public void setNodeId(Integer nodeId) { this.nodeId = nodeId; }
public Integer getLevel() { return level; }
public void setLevel(Integer level) { this.level = level; }
public String getText() { return text; }
public void setText(String text) { this.text = text; }}

复制代码


7.5、Rule(节点规则)


package com.ql.util.express.rule;
import java.util.ArrayList;import java.util.List;
/** * 节点规则 * Created by tianqiao on 16/12/8. */public class Rule extends Node{
/** * 代码 */ private String code; /** * 名称 */ private String name;
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
/** * 规则场景集合 */ private List<RuleCase> ruleCases = new ArrayList<RuleCase>();
/** * 获取规则案例集合 * @return */ public List<RuleCase> getRuleCases() { return ruleCases; }
/** * 添加规则案例集合 * @param ruleCase */ public void addRuleCases(RuleCase ruleCase) { this.ruleCases.add(ruleCase); }
public String toQl() { StringBuilder sb = new StringBuilder(); boolean first = true; for(RuleCase oneCase : ruleCases){ if(first){ sb.append(this.ruleCaseToScript("ql",oneCase)); first = false; }else{ sb.append("else ").append(this.ruleCaseToScript("ql",oneCase)); } } return sb.toString(); }
/** * 转为天窗 * @return */ public String toSkylight() { StringBuilder sb = new StringBuilder(); if(code!=null && name!=null){ sb.append(String.format("rule '%s'\nname '%s'\n",code,name)); } boolean first = true; for(RuleCase oneCase : ruleCases){ if(first){ sb.append(this.ruleCaseToScript("skylight",oneCase)); first = false; }else{ sb.append(this.ruleCaseToScript("skylight",oneCase)); } } return sb.toString(); }
/** * 转为树 * @return */ public String toTree() { StringBuilder sb = new StringBuilder(); sb.append(String.format("Rule:%s(%s),%d\n",code,name,getNodeId())); for(RuleCase ruleCase : ruleCases){ printLevel(ruleCase.getLevel(),sb); sb.append(String.format("Case:%s,%d\n","case",ruleCase.getNodeId())); printConitionTree(ruleCase.getCondition(),sb); for(Action action:ruleCase.getActions()){ printLevel(action.getLevel(),sb); sb.append(String.format("Action:%s,%d\n",action.getText(),action.getNodeId())); } } return sb.toString(); }
/** * 打印级别 * @param level * @param sb */ public void printLevel(int level,StringBuilder sb) { for(int i=0;i<level;i++){ sb.append("======"); } }
/** * 打印条件树 * @param root * @param sb */ private void printConitionTree(Condition root,StringBuilder sb) { printLevel(root.getLevel(),sb); sb.append(String.format("Condition:%s,%d\n",root.getText(),root.getNodeId())); if(root.getChildren()!=null) { for (Condition sub : root.getChildren()) { printConitionTree(sub,sb); } } }
/** * 规则案例转为脚本:QL表达式/skylight表达式 * @param scriptType * @param ruleCase * @return */ private String ruleCaseToScript(String scriptType, RuleCase ruleCase) { StringBuilder sb = new StringBuilder();
if(scriptType.equals("ql")) { for(Action action : ruleCase.getActions()){ sb.append(action.getText()).append(";\n"); } return String.format("if(%s)\n{\n%s}", ruleCase.getCondition().toString(), sb); }else if(scriptType.equals("skylight")){ for(Action action : ruleCase.getActions()){ sb.append(action.getText()).append("\n"); } return String.format("when %s\nthen %s", ruleCase.getCondition().toString(), sb); } return null; }}

复制代码

7.6、RuleCase(规则案例)

package com.ql.util.express.rule;
import java.util.List;
/** * Created by tianqiao on 16/12/6. */public class RuleCase extends Node{ /** * 行为集合 */ private List<Action> actions; /** * 条件 */ private Condition condition;
public RuleCase(Condition condition,List<Action> actions) { this.actions = actions; this.condition = condition; }
public List<Action> getActions() { return actions; }
public void setActions(List<Action> actions) { this.actions = actions; }
public Condition getCondition() { return condition; }
public void setCondition(Condition condition) { this.condition = condition; }}

复制代码


7.7、RuleManager(规则管理)

package com.ql.util.express.rule;
import com.ql.util.express.ExpressRunner;import com.ql.util.express.IExpressContext;import com.ql.util.express.parse.ExpressNode;import com.ql.util.express.parse.Word;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;
/** * Created by tianqiao on 16/12/8. */public class RuleManager { private static final Log log = LogFactory.getLog(RuleManager.class);
/** * 执行规则 * @param runner * @param rule * @param context * @param isCache * @param isTrace * @return */ public static RuleResult executeRule(ExpressRunner runner,Rule rule, IExpressContext<String,Object> context, boolean isCache, boolean isTrace) { RuleResult result = new RuleResult(); result.setRule(rule); Map<String, Boolean> traceMap = new LinkedHashMap<String, Boolean>(); result.setTraceMap(traceMap); Object actionResult = null; for(RuleCase ruleCase : rule.getRuleCases()) { Condition root = ruleCase.getCondition(); Boolean conditionResult = calculateCondition(runner,context,root,traceMap,isCache,isTrace,result); if(conditionResult==true){ for(Action action :ruleCase.getActions()){ try { traceMap.put(action.getNodeId()+"",true); actionResult = runner.execute(action.getText(),context,null,isCache,isTrace); } catch (Exception e) { result.setHasException(true); log.error("执行action出错:action=\n"+action.getText(),e); actionResult = null; } } break; } } result.setResult(actionResult); return result; }
/** * 计算条件 * @param runner * @param context * @param root * @param traceMap * @param isCache * @param isTrace * @param result * @return */ private static Boolean calculateCondition(ExpressRunner runner, IExpressContext<String, Object> context, Condition root, Map<String, Boolean> traceMap, boolean isCache, boolean isTrace, RuleResult result) { boolean isShortCircuit = runner.isShortCircuit(); String key = root.getNodeId()+""; if(root.getType()==ConditionType.Leaf){ String text = root.getText(); try { Boolean r = (Boolean) runner.execute(text,context,null,isCache,isTrace); traceMap.put(key,r); return r; } catch (Exception e) { result.setHasException(true); log.error("计算condition出错:condition=\n"+text,e); traceMap.put(key,false); return false; } }

Boolean unionLogicResult = null; ConditionType rootType = root.getType(); if(root.getChildren()!=null) { for (Condition sub : root.getChildren()) { Boolean subResult = calculateCondition(runner,context,sub,traceMap,isCache,isTrace, result); if(unionLogicResult==null){ unionLogicResult = subResult; }else{ if (rootType == ConditionType.And) { unionLogicResult = unionLogicResult && subResult; }else if (rootType == ConditionType.Or) { unionLogicResult = unionLogicResult || subResult; } } if(isShortCircuit) { if (rootType == ConditionType.And) { if(unionLogicResult==false){ break; } } if (rootType == ConditionType.Or) { if(unionLogicResult==true){ break; } } } } } traceMap.put(key,unionLogicResult); return unionLogicResult; }
/** * 创建规则 * @param root * @param words * @return */ public static Rule createRule(ExpressNode root, Word[] words) { ExpressNode ifNode = getIfRootNode(root); if (ifNode != null) { Rule rule = new Rule(); addRuleCaseByExpress(ifNode, rule, words); tagRuleConitionId(rule); return rule; } return null; }
/** * 标签规则添加条件ID * @param rule * @return */ private static Integer tagRuleConitionId(Rule rule) { Integer nodeId = 0; Integer level = 0; rule.setNodeId(nodeId++); rule.setLevel(level++); for(RuleCase ruleCase: rule.getRuleCases()){ ruleCase.setNodeId(nodeId++); ruleCase.setLevel(level); Condition root = ruleCase.getCondition(); nodeId = tagConditionNode(root,nodeId,level+1); List<Action>actions = ruleCase.getActions(); for(Action action:actions){ action.setLevel(level+1); action.setNodeId(nodeId++); } } return nodeId; }
/** * 标签条件节点 * @param root * @param nodeId * @param level * @return */ private static Integer tagConditionNode(Condition root,Integer nodeId,Integer level) { root.setLevel(level); root.setNodeId(nodeId++); if(root.getChildren()!=null) { for (Condition sub : root.getChildren()) { nodeId = tagConditionNode(sub, nodeId,level+1); } } return nodeId; }
/** * 根据表达式添加规则案例 * @param ifNode * @param rule * @param words */ private static void addRuleCaseByExpress(ExpressNode ifNode, Rule rule, Word[] words) { ExpressNode[] children = ifNode.getChildren(); ExpressNode condtion = null; ExpressNode action = null; ExpressNode nextCase = null;
//[0]ConditionNode int point = 0; ExpressNode first = children[point]; if (isNodeType(first, "CHILD_EXPRESS")) { condtion = first.getChildren()[0]; } else { condtion = first; } point++;
//[1]"then" if (isNodeType(children[point], "then")) { point++; }
//[2]ActionNode action = children[point]; point++;
//[3]"else" if (point < children.length && isNodeType(children[point], "else")) { point++;
//[4]IfNode if (point < children.length && isNodeType(children[point], "if")) { nextCase = children[point]; } } rule.addRuleCases(createRuleCase(condtion, action, words)); if (nextCase != null) { addRuleCaseByExpress(nextCase, rule, words); } }
/** * 获取根节点 * @param parent * @return */ private static ExpressNode getIfRootNode(ExpressNode parent) { if (isNodeType(parent, "if")) { return parent; } ExpressNode[] children = parent.getChildren(); if (children != null && children.length > 0) { for (ExpressNode child : children) { if (getIfRootNode(child) != null) { return child; } } } return null; }
/** * 创建规则案例 * @param condition * @param action * @param words * @return */ private static RuleCase createRuleCase(ExpressNode condition, ExpressNode action, Word[] words) {
Condition ruleCondition = new Condition(); transferCondition(condition, ruleCondition, words); List<Action> actions = new ArrayList<Action>(); if (isNodeType(action, "STAT_BLOCK")) { ExpressNode[] children = action.getChildren(); for (ExpressNode actionChild : children) { actions.add(new Action(makeActionString(actionChild,words))); } } else { actions.add(new Action(makeActionString(action,words))); } RuleCase ruleCase = new RuleCase(ruleCondition, actions); return ruleCase; }
/** * 翻译条件 * @param express * @param condition * @param words */ private static void transferCondition(ExpressNode express, Condition condition, Word[] words) { if (isNodeType(express, "&&")) { condition.setType(ConditionType.And); condition.setText("and"); ExpressNode[] children = express.getChildren(); for (ExpressNode child : children) { Condition subCondition = new Condition(); condition.addChild(subCondition); transferCondition(child, subCondition, words); }
} else if (isNodeType(express, "||")) { condition.setType(ConditionType.Or); condition.setText("or"); ExpressNode[] children = express.getChildren(); for (ExpressNode child : children) { Condition subCondition = new Condition(); condition.addChild(subCondition); transferCondition(child, subCondition, words); } } else { if (isNodeType(express, "CHILD_EXPRESS")||isNodeType(express, "STAT_BLOCK")||isNodeType(express, "STAT_SEMICOLON")) { //注意括号的情况 ExpressNode realExpress = express.getChildren()[0]; condition.setPrior(true); transferCondition(realExpress, condition, words); } else { condition.setType(ConditionType.Leaf); condition.setText(makeCondtionString(express, words));
} } }
/** * 是否是节点类型 * @param node * @param type * @return */ private static boolean isNodeType(ExpressNode node, String type) { return node.getNodeType().getName().equals(type);
}
/** * 匹配行为 * @param express * @param words * @return */ private static String makeActionString(ExpressNode express, Word[] words) { int min = getMinNode(express); int max = getMaxNode(express); //最后需要匹配一个)括号的问题,另外还有是无参数的情况 function() while(max+1<words.length && (words[max+1].word.equals(")")||words[max+1].word.equals("("))){ max++; } if(min<0) min = 0; if(max>=words.length) max = words.length-1; StringBuilder result = new StringBuilder(); int balance = 0;//小括号的相互匹配数量 for(int i=min;i<=max;i++) { if(words[i].word.equals("(")){ balance++; }else if(words[i].word.equals(")")){ balance--; } if(balance<0){ balance++;//当前字符不合并,恢复成0,用于最终的判断 break; } result.append(words[i].word); if(words[i].word.equals("return")){ result.append(" "); } } if(balance!=0){ System.out.println(result); throw new RuntimeException("括号匹配异常"); } return result.toString(); }
/** * 匹配条件 * @param express * @param words * @return */ private static String makeCondtionString(ExpressNode express,Word[] words) { int min = getMinNode(express); int max = getMaxNode(express); //最后需要匹配一个括号的问题 while(max+1<words.length && (words[max+1].word.equals(")")||words[max+1].word.equals("("))){ max++; } if(min<0) min = 0; if(max>=words.length) max = words.length-1; StringBuilder result = new StringBuilder(); int balance = 0;//小括号的相互匹配数量 for(int i=min;i<=max;i++) { if(words[i].word.equals("(")){ balance++; }else if(words[i].word.equals(")")){ balance--; } if(balance<0){ balance++;//当前字符不合并,恢复成0,用于最终的判断 break; } result.append(words[i].word); } if(balance!=0){ throw new RuntimeException("括号匹配异常"); } return result.toString(); }
/** * 获取最小的节点 * @param express * @return */ private static int getMinNode(ExpressNode express) { if(express.getChildren()==null||express.getChildren().length==0){ return express.getWordIndex(); } int wordIndex = express.getWordIndex(); if(express.getChildren()!=null){ for(ExpressNode child : express.getChildren()){ int childIndex = getMinNode(child); if(wordIndex==-1 || childIndex<wordIndex){ wordIndex = childIndex; } } } return wordIndex; }
/** * 获取最大节点 * @param express * @return */ private static int getMaxNode(ExpressNode express) { if(express.getChildren()==null||express.getChildren().length==0){ return express.getWordIndex(); } int wordIndex = express.getWordIndex(); if(express.getChildren()!=null){ for(ExpressNode child : express.getChildren()){ int childIndex = getMaxNode(child); if(childIndex>wordIndex){ wordIndex = childIndex; } } } return wordIndex; }
/** * 创建条件 * @param condition * @param words * @return */ public static Condition createCondition(ExpressNode condition, Word[] words) { Condition ruleCondition = new Condition(); transferCondition(condition, ruleCondition, words); return ruleCondition; } }

复制代码


7.8、RuleResult(规则结果)

package com.ql.util.express.rule;
import java.util.Map;
/** * 规则结果 * Created by tianqiao on 16/12/12. */public class RuleResult { /** * 是否有异常 */ private boolean hasException = false; /** * 规则 */ private Rule rule; /** * 轨迹集合 */ private Map<String,Boolean> traceMap; /** * 结果对象 */ private Object result; /** * 脚本 */ private String script; public String getScript() { return script; } public void setScript(String script) { this.script = script; } public boolean isHasException() { return hasException; } public void setHasException(boolean hasException) { this.hasException = hasException; } public Rule getRule() { return rule; }
public void setRule(Rule rule) { this.rule = rule; }
public Map<String, Boolean> getTraceMap() { return traceMap; }
public void setTraceMap(Map<String, Boolean> traceMap) { this.traceMap = traceMap; }
public Object getResult() { return result; }
public void setResult(Object result) { this.result = result; }}

复制代码


7.9、小结


  • 规则拆解为行为,条件(节点,类型),管理器,结果

发布于: 2021 年 01 月 15 日阅读数: 21
用户头像

小胜靠智,大胜靠德 2019.06.27 加入

历经创业、京东、腾讯、滴滴公司,希望能够有些东西一起分享。公众号:小诚信驿站,微信/CSDN搜索:wolf_love666

评论

发布
暂无评论
案例研究之聊聊 QLExpress 源码 (七)