案例研究之聊聊 QLExpress 源码 (七)
发布于: 2021 年 01 月 15 日
规则拆解为行为,条件(节点,类型),管理器,结果, 如果我们要实现规则的话可以借鉴其拆解结构,进行节点规则管理。
七、规则模块(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
版权声明: 本文为 InfoQ 作者【小诚信驿站】的原创文章。
原文链接:【http://xie.infoq.cn/article/b5ce0618e6551219068631408】。文章转载请联系作者。
小诚信驿站
关注
小胜靠智,大胜靠德 2019.06.27 加入
历经创业、京东、腾讯、滴滴公司,希望能够有些东西一起分享。公众号:小诚信驿站,微信/CSDN搜索:wolf_love666
评论