写点什么

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

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

匹配模块属于规则引擎中的一个核心点,可以通过提前约定好的模式节点以及匹配的正则表达式来提供通用化的能力支持,更好的沉淀一些基础的能力,达到框架的通用性。

五、匹配模块(match)

5.1、IDataNode 数据节点


package com.ql.util.express.match;


/** * 数据节点 * @author xiaochengxinyizhan */public interface IDataNode { /** * 设置节点类型 * @param type */ void setNodeType(INodeType type);
/** * 设置树类型 * @param findNodeType */ void setTreeType(INodeType findNodeType);
/** * 获取节点类型 * @return */ INodeType getNodeType();
/** * 获取树类型 * @return */ INodeType getTreeType();
/** * 添加左孩子数据节点 * @param ref */ void addLeftChild(IDataNode ref);
/** * 创建表达式节点 * @param aType * @param aValue * @return * @throws Exception */ IDataNode createExpressNode(INodeType aType, String aValue) throws Exception;
/** * 获取值 * @return */ String getValue();
/** * 设置对象值 * @param value */ void setObjectValue(Object value);}

复制代码

5.2、INodeType 数据节点类型

package com.ql.util.express.match;

/** * 匹配类型 * @author xuannan * */public interface INodeType { /** * 获取名字 * @return */ String getName();
/** * 获取节点类型管理器 * @return */ INodeTypeManager getManager();
/** * 获取父节点 * @return */ QLPatternNode getPatternNode();}

复制代码

5.3、INodeTypeManager 节点类型管理器

package com.ql.util.express.match;
/** * 节点类型管理器 * @author xiaochengxinyizhan */public interface INodeTypeManager { /** * 查找节点类型 * @param name * @return */ INodeType findNodeType(String name);}

复制代码

5.4、QLMatchResult 匹配结果

package com.ql.util.express.match;
import java.util.ArrayList;import java.util.List;
/** * 匹配结果 * * @author xiaochengxinyizhan */public class QLMatchResult { /** * 匹配结果树集合 */ private List<QLMatchResultTree> matchs = new ArrayList<QLMatchResultTree>(); /** * 匹配最后索引 */ private int matchLastIndex;
/** * 清除匹配结果树集合 */ public void clear() { this.matchLastIndex = 0; this.matchs.clear(); }
/** * 将匹配结果树集合字符串输出 * @return */ @Override public String toString() { StringBuilder builder = new StringBuilder(); for (QLMatchResultTree item : matchs) { item.printNode(builder, 1); } return builder.toString(); }
/** * 获取匹配结果树集合 * @return */ public List<QLMatchResultTree> getMatchs() { return matchs; }
/** * 添加匹配结果树单个对象 * @param tree * @return */ public QLMatchResult addQLMatchResultTree(QLMatchResultTree tree) { this.matchs.add(tree); return this; }
/** * 添加匹配结果树集合 * @param aList * @return */ public QLMatchResult addQLMatchResultTreeList(List<QLMatchResultTree> aList) { this.matchs.addAll(aList); return this; }
/** * 获取匹配结果树集合大小 * @return */ public int getMatchSize() { return this.matchs.size(); }
/** * 获取匹配最后的索引 * @return */ public int getMatchLastIndex() { return matchLastIndex; }
/** * 设置匹配的最后索引 * @param index * @return */ public QLMatchResult setMatchLastIndex(int index) { this.matchLastIndex = index; return this; }}

复制代码

5.5、QLMatchResultTree 匹配结果树

package com.ql.util.express.match;
import java.util.ArrayList;import java.util.List;
/** * 匹配结果树 * @author xiaochengxinyizhan */public class QLMatchResultTree{ /** * 节点类型 */ INodeType matchNodeType; /** * 数据节点 */ IDataNode ref; /** * 目标节点类型 */ INodeType targetNodeType; /** * 左子树 */ private List<QLMatchResultTree> left; /** * 右子树 */ private List<QLMatchResultTree> right;
/** * 匹配结果树构造函数 * @param aNodeType * @param aRef * @param aTargetNodeType */ public QLMatchResultTree(INodeType aNodeType,IDataNode aRef,INodeType aTargetNodeType){ this(aNodeType,aRef); this.targetNodeType = aTargetNodeType; }
/** * 匹配结果树构造函数 * @param aNodeType * @param aRef */ public QLMatchResultTree(INodeType aNodeType,IDataNode aRef){ this.matchNodeType = aNodeType; this.ref = aRef; }
/** * 获取数据节点引用 * @return */ public IDataNode getRef() { return ref; }
/** * 获取左子树 * @return */ public List<QLMatchResultTree> getLeft(){ return this.left; }
/** * 添加左子树 * @param node */ public void addLeft(QLMatchResultTree node){ if(this.left == null){ this.left = new ArrayList<QLMatchResultTree>(); } this.left.add(node); }
/** * 添加左子树集合 * @param list */ public void addLeftAll(List<QLMatchResultTree> list){ if(this.left == null){ this.left = new ArrayList<QLMatchResultTree>(); } this.left.addAll(list); }
/** * 添加右子树集合 * @param list */ public void addRightAll(List<QLMatchResultTree> list){ if(this.right == null){ this.right = new ArrayList<QLMatchResultTree>(); } this.right.addAll(list); }
/** * 转表达式节点类型 * @param sourceNode * @param targetType * @return */ public IDataNode transferExpressNodeType(IDataNode sourceNode,INodeType targetType){ sourceNode.setNodeType(targetType); if(targetType == targetType.getManager().findNodeType("CONST_STRING")){ sourceNode.setObjectValue(sourceNode.getValue()); sourceNode.setTreeType(targetType.getManager().findNodeType("CONST")); } return sourceNode; }
/** * 构建表达式节点树 */ public void buildExpressNodeTree(){ if(this.targetNodeType != null){ transferExpressNodeType(this.ref,this.targetNodeType); } if(this.left != null){ for (QLMatchResultTree item : left) { this.ref.addLeftChild(item.ref); item.buildExpressNodeTree(); } } if (this.right != null) { for (QLMatchResultTree item : right) { this.ref.addLeftChild(item.ref); item.buildExpressNodeTree(); } } }
/** * 将节点输出 * @return */ @Override public String toString(){ StringBuilder builder = new StringBuilder(); printNode(builder,1); return builder.toString(); }
/** * 获取节点打印顺序 * @param builder * @param level */ public void printNode(StringBuilder builder, int level) { builder.append(level + ":"); for (int i = 0; i < level; i++) { builder.append(" "); } builder.append(ref.getValue() + ":" + this.matchNodeType.getName()) .append("\n"); if (this.left != null) { for (QLMatchResultTree item : this.left) { item.printNode(builder, level + 1); } } if (this.right != null) { for (QLMatchResultTree item : this.right) { item.printNode(builder, level + 1); } } }}

复制代码

5.6、QLPattern 模式(核心内容)

package com.ql.util.express.match;
import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicLong;
import com.ql.util.express.exception.QLCompileException;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;
/** * 模式 * @author xiaochengxinyizhan */public class QLPattern {
private static final Log log = LogFactory.getLog(QLPattern.class); /** * 优化栈的递归深度 */ public static boolean optimizeStackDepth = true; /** * 打印栈的最大深度 */ public static boolean printStackDepth = false;
/** * 创建模式节点 * @param nodeTypeManager * @param name * @param pattern * @return * @throws Exception */ public static QLPatternNode createPattern(INodeTypeManager nodeTypeManager,String name,String pattern) throws Exception{ return new QLPatternNode(nodeTypeManager,name,pattern); }
/** * 查找匹配的语法规范 * @param aManager * @param pattern * @param nodes * @param point * @return * @throws Exception */ public static QLMatchResult findMatchStatement(INodeTypeManager aManager,QLPatternNode pattern ,List<? extends IDataNode> nodes,int point) throws Exception{ //最大的原子匹配节点 AtomicLong maxMatchPoint = new AtomicLong(); //最大的深度,初始化默认值1 AtomicLong maxDeep = new AtomicLong(1); //匹配结果缓存行,大小默认5 QLMatchResultCache resultCache =new QLMatchResultCache(5); //数组集合缓存 ArrayListCache<QLMatchResultTree> arrayListCache = new ArrayListCache<QLMatchResultTree>(50); //匹配参数包 MatchParamsPack staticParams = new MatchParamsPack(aManager, nodes, maxDeep, maxMatchPoint,resultCache,arrayListCache); //查找匹配语法和添加根优化栈 QLMatchResult result = findMatchStatementWithAddRootOptimizeStack(staticParams, pattern, point, true, 1); if(printStackDepth) { log.warn("递归堆栈深度:" + maxDeep.longValue() + " 重用QLMatchResult次数:" + resultCache.fetchCount + " 新建QLMatchResult次数:" + resultCache.newCount + " 新建ArrayList数量:" + arrayListCache.newCount);
} //只能有唯一的匹配语法规范 if(result == null || result.getMatchSize() == 0){ throw new QLCompileException("程序错误,不满足语法规范,没有匹配到合适的语法,最大匹配致[0:" + (maxMatchPoint.longValue()-1) +"]"); }else if(result != null && result.getMatchSize() != 1){ throw new QLCompileException("程序错误,不满足语法规范,必须有一个根节点:" + pattern + ",最大匹配致[0:" + (maxMatchPoint.longValue()-1) +"]"); } return result; }
/** * 查找匹配语法规范和添加根优化栈 * @param staticParams * @param pattern * @param point * @param isRoot * @param deep * @return * @throws Exception */ private static QLMatchResult findMatchStatementWithAddRootOptimizeStack(MatchParamsPack staticParams,QLPatternNode pattern ,int point,boolean isRoot,int deep) throws Exception{ //节点类型管理器 INodeTypeManager aManager = staticParams.aManager; //静态参数的节点集合 List<? extends IDataNode> nodes = staticParams.nodes; //静态参数的最大匹配点 AtomicLong maxMatchPoint = staticParams.maxMatchPoint; //静态参数的最大深度 AtomicLong maxDeep = staticParams.maxDeep; //mark maxDeep //标记最大深度 deep++; //如果深度大于最大深度的值,将最大深度的值重新赋值 if (deep > maxDeep.longValue()){ maxDeep.set(deep); } QLMatchResult result = null; List<QLMatchResultTree> tempList = null; int count = 0; //最后节点 int lastPoint = point; while(true){ QLMatchResult tempResult = null; //如果模式的匹配模式和匹配模式的默认策略相同 if (pattern.matchMode == MatchMode.DETAIL) { .... } //如果模式的匹配模式和匹配模式的并且相同 else if (pattern.matchMode == MatchMode.AND) { .... } //如果模式的匹配模式和匹配模式的或相同 else if (pattern.matchMode == MatchMode.OR) { .... } else{ throw new QLCompileException("不正确的类型:" + pattern.matchMode.toString()); }
if(tempResult == null){ if(count >= pattern.minMatchNum && count <=pattern.maxMatchNum){ //正确匹配 result = staticParams.resultCache.fetch(); if(tempList != null){ result.addQLMatchResultTreeList(tempList); } result.setMatchLastIndex(lastPoint); }else{ result = null; } break; }else{ if(tempList == null){ tempList =staticParams.arrayListCache.fetch(); } lastPoint = tempResult.getMatchLastIndex(); if(pattern.isTreeRoot == true){ if(tempResult.getMatchSize() > 1){ throw new QLCompileException("根节点的数量必须是1"); } if(tempList.size() == 0){ tempList.addAll(tempResult.getMatchs()); }else{ tempResult.getMatchs().get(0).addLeftAll(tempList); //为了能回收QLMatchResult对象,这个地方必须进行数组拷贝 tempList = staticParams.arrayListCache.fetch(); tempList.addAll(tempResult.getMatchs()); } }else{ tempList.addAll(tempResult.getMatchs()); } }
/** 归还QLMatchResult */ if(tempResult != null){ staticParams.resultCache.sendBack(tempResult); tempResult = null; }
count = count + 1; if(count == pattern.maxMatchNum){ result = staticParams.resultCache.fetch(); if(tempList != null){ result.addQLMatchResultTreeList(tempList); } result.setMatchLastIndex(lastPoint); break; } } if(result != null && pattern.isSkip == true){ //忽略跳过所有匹配到的节点 result.getMatchs().clear(); } //封装返回结果信息 if(result != null && result.getMatchSize() >0 && pattern.rootNodeType != null){ QLMatchResultTree tempTree = new QLMatchResultTree(pattern.rootNodeType,nodes.get(0).createExpressNode(pattern.rootNodeType,null)); tempTree.addLeftAll(result.getMatchs()); result.getMatchs().clear(); result.getMatchs().add(tempTree); } //如果临时数据不为空,则清空内存数据 if (tempList != null){ staticParams.arrayListCache.sendBack(tempList); } return result; }//打印日志轨迹信息 public static void traceLog(QLPatternNode pattern, QLMatchResult result, List<? extends IDataNode> nodes, int point,int matchCount) { if (log.isTraceEnabled() && (pattern.matchMode ==MatchMode.DETAIL || pattern.matchMode == MatchMode.AND && matchCount > 1 && pattern.name.equals("ANONY_PATTERN") == false )) { log.trace("匹配--" + pattern.name +"[" + point + ":" + (result.getMatchLastIndex() -1)+ "]:" + pattern); } } //封装匹配参数类 public static class MatchParamsPack { INodeTypeManager aManager; List<? extends IDataNode> nodes; AtomicLong maxDeep; AtomicLong maxMatchPoint; QLMatchResultCache resultCache; ArrayListCache arrayListCache; //封装匹配参数结果 public MatchParamsPack(INodeTypeManager aManager, List<? extends IDataNode> nodes, AtomicLong maxDeep, AtomicLong maxMatchPoint,QLMatchResultCache aResultCache,ArrayListCache aArrayListCache) { this.aManager = aManager; this.nodes = nodes; this.maxDeep = maxDeep; this.maxMatchPoint = maxMatchPoint; this.resultCache = aResultCache; this.arrayListCache = aArrayListCache; } }//QL匹配结果缓存 public static class QLMatchResultCache { public int newCount =0; public int fetchCount =0;
private QLMatchResult[] cache ; private int len= 10; private int point =9; //构造缓存结果 public QLMatchResultCache(int aLen){ this.len = aLen; this.point = this.len -1; cache = new QLMatchResult[this.len ]; for (int i=0;i<this.len;i++){ cache[i] = new QLMatchResult(); } }//拉取QL匹配结果 public QLMatchResult fetch() { QLMatchResult result = null; if (point >=0) { result = cache[point]; cache[point] = null; point = point - 1; fetchCount++; } else { result = new QLMatchResult(); newCount++; } return result; } //清除缓存数据 public void sendBack(QLMatchResult result){ if (this.point <this.len -1){ this.point = this.point + 1; cache[this.point] = result; cache[this.point].clear(); } }
}//数组列表缓存 public static class ArrayListCache<T> {
public int newCount =0; public int fetchCount =0;
private List<T>[] cache ; private int len= 50; private int point =49; //构造缓存数据 public ArrayListCache(int aLen){ this.len = aLen; this.point = this.len -1; cache = new List[this.len]; for (int i=0;i<this.len;i++){ cache[i] = new ArrayList<T>(); } } //拉取数据列表信息 public List<T> fetch() { List<T> result = null; //如果有下标则从缓存中获取 if (point >=0) { result = cache[point]; cache[point] = null; point = point - 1; fetchCount++; } else { //否则直接计数 result = new ArrayList<T>(); newCount++; } return result; } //清除缓存 public void sendBack(List<T> result){ if (this.point <this.len -1){ this.point = this.point + 1; cache[this.point] = result; cache[this.point].clear(); } } }}
复制代码

5.7、QLPatternNode 模式节点


package com.ql.util.express.match;
import java.util.ArrayList;import java.util.List;
import com.ql.util.express.exception.QLCompileException;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;//定义匹配模式enum MatchMode{ AND,OR,DETAIL,NULL}public class QLPatternNode{ private static final Log log = LogFactory.getLog(QLPatternNode.class); INodeTypeManager nodeTypeManager; String name; /** * 原始的字符串 */ String orgiContent; /** * 匹配模式 */ MatchMode matchMode =MatchMode.NULL ; /** * 是否一个子匹配模式 */ boolean isChildMode = false; /** * 层次 */ int level =0; /** * 是否根节点,例如:if^ */ protected boolean isTreeRoot =false; /** * 最小匹配次数,0..n */ protected int minMatchNum =1; /** * 最大匹配次数 */ protected int maxMatchNum =1; /** * 匹配类型,例如 ID,if,SELECT */ protected INodeType nodeType; /** * 匹配到的节点需要转换成的类型,例如 ID -》CONST_STRING */ protected INodeType targetNodeType; /** * 需要转为的虚拟类型,例如:(ID$(,$ID)*)#COL_LIST */ protected INodeType rootNodeType; /** * 是否匹配成功,但在输出的时候忽略,用"~"表示 * CONST$(,~$CONST)* */ protected boolean isSkip =false; /** * 取反,例如:+@,匹配不是+的所有字符 */ protected boolean blame = false;
public boolean canMergeDetail(){ if (QLPattern.optimizeStackDepth && this.matchMode == MatchMode.DETAIL && this.name.equals("ANONY_PATTERN") && this.nodeType.getPatternNode() != null && this.isSkip == false && this.blame ==false && this.isChildMode == false && this.isTreeRoot == false && this.minMatchNum ==1 && this.maxMatchNum == 1){ return true; } return false; } /** * 子匹配模式 */ List<QLPatternNode> children = new ArrayList<QLPatternNode>(); protected QLPatternNode(INodeTypeManager aManager,String aName,String aOrgiContent) throws Exception{ this(aManager,aName,aOrgiContent,false,1);// if(this.toString().equals(aOrgiContent)==false){ //throw new QLCompileException("语法定义解析后的结果与原始值不一致,原始值:"+ aOrgiContent + " 解析结果:" + this.toString()); //log.error(("语法定义解析后的结果与原始值不一致,原始值:"+ aOrgiContent + " 解析结果:" + this.toString()));// } } //构造QLPatternNode函数 protected QLPatternNode(INodeTypeManager aManager,String aName,String aOrgiContent,boolean aIsChildMode,int aLevel) throws Exception{ this.nodeTypeManager = aManager; this.name = aName; this.orgiContent = aOrgiContent; this.isChildMode = aIsChildMode; this.level = aLevel; this.splitChild(); for (int i=0;i< children.size();i++){ QLPatternNode t = children.get(i); if(t.canMergeDetail()) { this.children.set(i,t.getNodeType().getPatternNode()); if(t.getNodeType().getPatternNode().getNodeType() == null){ t.getNodeType().getPatternNode().nodeType = t.getNodeType(); } } }
} //拆解子匹配模式 public void splitChild() throws Exception{ if(log.isTraceEnabled()){ String str =""; for(int i=0;i<this.level;i++){ str = str + " "; } //log.trace("分解匹配模式[LEVEL="+ this.level +"]START:" + str + this.orgiContent); } String orgStr = this.orgiContent; if(orgStr.equals("(") || orgStr.equals(")") || orgStr.equals("|")||orgStr.equals("||")||orgStr.equals("/**") || orgStr.equals("**/")||orgStr.equals("*")){ this.matchMode = MatchMode.DETAIL; this.nodeType = this.nodeTypeManager.findNodeType(orgStr); return ; } String tempStr =""; int count =0; //具体实现逻辑 for(int i=0;i<orgStr.length();i++){ if (orgStr.charAt(i) == '(') { tempStr = tempStr + orgStr.charAt(i); count = count + 1; }else if(orgStr.charAt(i) == ')'){ tempStr = tempStr + orgStr.charAt(i); count = count - 1; }else if(count > 0){ tempStr = tempStr + orgStr.charAt(i); }else if(orgStr.charAt(i) == '$'){ if (this.matchMode != MatchMode.NULL && this.matchMode != MatchMode.AND) { throw new QLCompileException("不正确的模式串,在一个匹配模式中不能|,$并存,请使用字串模式:" + orgStr); } children.add(new QLPatternNode(this.nodeTypeManager,"ANONY_PATTERN",tempStr, false,this.level + 1)); this.matchMode = MatchMode.AND; tempStr = ""; }else if(orgStr.charAt(i) == '|'){ if (this.matchMode != MatchMode.NULL && this.matchMode != MatchMode.OR) { throw new QLCompileException("不正确的模式串,在一个匹配模式中不能|,$并存,请使用字串模式:" + orgStr); } children.add(new QLPatternNode(this.nodeTypeManager,"ANONY_PATTERN",tempStr, false,this.level + 1)); this.matchMode = MatchMode.OR; tempStr = ""; }else if(orgStr.charAt(i) == '#'){ this.rootNodeType = this.nodeTypeManager.findNodeType(orgStr.substring(i+1)); break; }else { tempStr = tempStr + orgStr.charAt(i); } } // 处理没有()的内容 if (count > 0) { throw new QLCompileException("不正确的模式串,(没有找到对应的):" + orgStr); } if(this.children.size() > 0){ children.add(new QLPatternNode(this.nodeTypeManager,"ANONY_PATTERN",tempStr, false,this.level + 1)); tempStr =""; } //需要剔除乘法*的情况 if(tempStr.endsWith("*") && tempStr.length() >1){ this.minMatchNum = 0; this.maxMatchNum = Integer.MAX_VALUE; tempStr = tempStr.substring(0,tempStr.length() -1); } //判断是否是结尾符号的情况 if(tempStr.endsWith("}")){ int index = tempStr.lastIndexOf("{"); if(index > 0){ String numStr = tempStr.substring(index + 1,tempStr.length() - 1); int index2 = numStr.indexOf(':'); if (index2 > 0) { this.minMatchNum = Integer.parseInt(numStr.substring(0, index2)); this.maxMatchNum = Integer.parseInt(numStr.substring(index2 + 1)); } else { this.minMatchNum = Integer.parseInt(numStr); this.maxMatchNum = Integer.parseInt(numStr); } tempStr = tempStr.substring(0,index); } } if(tempStr.endsWith("^")==true && tempStr.length() > 1){ this.isTreeRoot = true; tempStr = tempStr.substring(0,tempStr.length() -1); }
//判断~情况 if(tempStr.endsWith("~") && tempStr.length() >1){ this.isSkip = true; tempStr = tempStr.substring(0,tempStr.length() -1); } //判断@情况 if(tempStr.endsWith("@") && tempStr.length() >1){ this.blame = true; tempStr = tempStr.substring(0,tempStr.length() -1); } //处理(ABC|bcd)模式 if(tempStr.length() > 2 && tempStr.charAt(0)=='(' && tempStr.charAt(tempStr.length() - 1) ==')'){ this.isChildMode = true; this.children.add(new QLPatternNode(this.nodeTypeManager,"ANONY_PATTERN",tempStr.substring(1, tempStr.length() - 1), false,this.level + 1)); this.matchMode = MatchMode.AND; tempStr = ""; } int index = tempStr.indexOf("->"); if (index > 0) { this.targetNodeType = this.nodeTypeManager.findNodeType(tempStr.substring(index + 2)); tempStr = tempStr.substring(0, index); } if (tempStr.length() > 0) { this.matchMode = MatchMode.DETAIL; this.nodeType = this.nodeTypeManager.findNodeType(tempStr); } } //获取子节点 public List<QLPatternNode> getChildren(){ return this.children; } //获取节点类型 public INodeType getNodeType(){ return this.nodeType; } //是否是明细模式 public boolean isDetailMode(){ return this.matchMode == MatchMode.DETAIL; } //是否是And模式 public boolean isAndMode(){ return this.matchMode == MatchMode.AND; } //转字符串 public String toString(){ String result =""; if(this.matchMode == MatchMode.AND){ result = this.joinStringList(this.children,"$"); }else if(this.matchMode ==MatchMode.OR){ result = this.joinStringList(this.children,"|"); }else{ result = this.nodeType.getName(); } if(this.targetNodeType != null){ result = result +"->" + this.targetNodeType.getName(); } if(this.isChildMode == true){ result ="("+ result + ")"; } if(this.isSkip){ result = result +'~'; } if(this.blame){ result = result +'@'; } if(this.isTreeRoot){ result = result +'^'; } if(this.minMatchNum == 0 && this.maxMatchNum == Integer.MAX_VALUE){ result = result +'*'; }else if(this.minMatchNum == this.maxMatchNum && this.maxMatchNum > 1) { result = result + "{" + this.maxMatchNum +"}"; }else if(this.minMatchNum != this.maxMatchNum){ result = result + "{" + this.minMatchNum +":" + this.maxMatchNum +"}"; } if(this.rootNodeType != null){ result = result + '#'+ this.rootNodeType.getName(); } return result; } //加入字符串列表 public String joinStringList(List<QLPatternNode> list,String splitChar){ StringBuffer buffer = new StringBuffer(); for(int i=0;i<list.size();i++){ if(i>0){buffer.append(splitChar);} buffer.append(list.get(i)); } return buffer.toString(); }}


复制代码

5.8、小结

  • 数据节点和匹配的模式定义

  • 约定好通用的符号来帮助框架能够使用的简单方便


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

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

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

评论

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