# Conflicts:
#	liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java
This commit is contained in:
ousinka
2022-11-04 14:29:43 +08:00
22 changed files with 686 additions and 253 deletions

View File

@@ -16,170 +16,171 @@ import java.util.function.Predicate;
/**
* 节点类型枚举
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public enum NodeTypeEnum {
COMMON("common","普通", false, NodeComponent.class),
COMMON("common", "普通", false, NodeComponent.class),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
IF("if", "条件", false, NodeIfComponent.class),
IF("if", "条件", false, NodeIfComponent.class),
FOR("for","循环次数", false, NodeForComponent.class),
FOR("for", "循环次数", false, NodeForComponent.class),
WHILE("while", "循环条件", false, NodeWhileComponent.class),
WHILE("while", "循环条件", false, NodeWhileComponent.class),
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
SCRIPT("script","脚本", true, ScriptCommonComponent.class),
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class)
;
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class);
private String code;
private String name;
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
private boolean isScript;
private String code;
private String name;
private Class<? extends NodeComponent> mappingClazz;
private boolean isScript;
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
private Class<? extends NodeComponent> mappingClazz;
public String getCode() {
return code;
}
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public void setCode(String code) {
this.code = code;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isScript() {
return isScript;
}
public void setName(String name) {
this.name = name;
}
public void setScript(boolean script) {
isScript = script;
}
public boolean isScript() {
return isScript;
}
public Class<? extends NodeComponent> getMappingClazz() {
return mappingClazz;
}
public void setScript(boolean script) {
isScript = script;
}
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public Class<? extends NodeComponent> getMappingClazz() {
return mappingClazz;
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
return e;
}
}
return null;
}
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz){
Class<?> superClazz = clazz;
while(true){
superClazz = superClazz.getSuperclass();
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")){
break;
}
if(superClazz.equals(Object.class)){
return null;
}
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
return e;
}
}
return null;
}
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz) {
Class<?> superClazz = clazz;
while (true) {
superClazz = superClazz.getSuperclass();
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")) {
break;
}
if (superClazz.equals(Object.class)) {
return null;
}
}
public static NodeTypeEnum guessType(Class<?> clazz){
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
if (nodeType == null){
//尝试从类声明处进行推断
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
if (liteflowCmpDefine != null){
//类声明方式中@LiteflowMethod是无需设置nodeId的
//但是如果设置了那么核心逻辑其实是取类上定义的id的
//这种可以运行,但是理解起来不大好理解,所以给出提示,建议不要这么做
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null){
return StrUtil.isNotBlank(liteflowMethod.nodeId());
}else{
return false;
}
});
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
if (mixDefined){
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
clazz.getName());
}
public static NodeTypeEnum guessType(Class<?> clazz) {
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
if (nodeType == null) {
//尝试从类声明处进行推断
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
if (liteflowCmpDefine != null) {
//类声明方式中@LiteflowMethod是无需设置nodeId的
//但是如果设置了那么核心逻辑其实是取类上定义的id的
//这种可以运行,但是理解起来不大好理解,所以给出提示,建议不要这么做
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null) {
return StrUtil.isNotBlank(liteflowMethod.nodeId());
} else {
return false;
}
});
if (mixDefined) {
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
clazz.getName());
}
//在返回之前,还要对方法级别的@LiteflowMethod进行检查如果存在方法上的类型与类上的不一致时给予警告信息
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null){
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())){
differenceTypeMethod.set(method);
return true;
}else{
return false;
}
}else{
return false;
}
});
//在返回之前,还要对方法级别的@LiteflowMethod进行检查如果存在方法上的类型与类上的不一致时给予警告信息
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null) {
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())) {
differenceTypeMethod.set(method);
return true;
} else {
return false;
}
} else {
return false;
}
});
//表示存在不一样的类型
if (hasDifferenceNodeType){
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
clazz.getName(), differenceTypeMethod.get().getName());
}
//表示存在不一样的类型
if (hasDifferenceNodeType) {
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
clazz.getName(), differenceTypeMethod.get().getName());
}
return liteflowCmpDefine.value();
}
return liteflowCmpDefine.value();
}
//再尝试声明式组件这部分的推断
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
//再尝试声明式组件这部分的推断
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
if (liteflowMethod != null) {
nodeType = liteflowMethod.nodeType();
}
}
return nodeType;
}
if (liteflowMethod != null) {
nodeType = liteflowMethod.nodeType();
}
}
return nodeType;
}
}

View File

@@ -35,7 +35,8 @@ public abstract class BaseJsonFlowParser implements FlowParser {
JsonNode flowJsonNode = JsonUtil.parseObject(content);
jsonObjectList.add(flowJsonNode);
}
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
ParserHelper.parseNodeJson(jsonObjectList);
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
}
/**

View File

@@ -36,8 +36,8 @@ public abstract class BaseXmlFlowParser implements FlowParser {
documentList.add(document);
}
Consumer<Element> parseOneChainConsumer = this::parseOneChain;
ParserHelper.parseDocument(documentList, CHAIN_NAME_SET, parseOneChainConsumer);
ParserHelper.parseNodeDocument(documentList);
ParserHelper.parseChainDocument(documentList, CHAIN_NAME_SET, this::parseOneChain);
}
/**

View File

@@ -39,7 +39,8 @@ public abstract class BaseYmlFlowParser implements FlowParser {
}
Consumer<JsonNode> parseOneChainConsumer = this::parseOneChain;
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET,parseOneChainConsumer);
ParserHelper.parseNodeJson(jsonObjectList);
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, parseOneChainConsumer);
}
protected JsonNode convertToJson(String yamlString) {

View File

@@ -1,11 +1,9 @@
package com.yomahub.liteflow.parser.helper;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.yomahub.liteflow.annotation.*;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.builder.prop.NodePropBean;
@@ -87,35 +85,13 @@ public class ParserHelper {
/**
* xml 形式的主要解析过程
*
* @param documentList documentList
* @param chainNameSet 用于去重
* @param parseOneChainConsumer parseOneChain 函数
*/
public static void parseDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer) {
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
documentList.forEach(document -> {
// 解析chain节点
List<Element> chainList = document.getRootElement().elements(CHAIN);
//先在元数据里放上chain
chainList.forEach(e -> {
//校验加载的 chainName 是否有重复的
//TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
});
});
// 清空
chainNameSet.clear();
/**
* xml 形式的主要解析过程
* @param documentList documentList
*/
public static void parseNodeDocument(List<Document> documentList) {
for (Document document : documentList) {
Element rootElement = document.getRootElement();
Element nodesElement = rootElement.element(NODES);
@@ -143,38 +119,67 @@ public class ParserHelper {
ParserHelper.buildNode(nodePropBean);
}
}
//解析每一个chain
List<Element> chainList = rootElement.elements(CHAIN);
chainList.forEach(parseOneChainConsumer);
}
}
public static void parseJsonNode(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer) {
public static void parseDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer) {
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
documentList.forEach(document -> {
// 解析chain节点
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
List<Element> chainList = document.getRootElement().elements(CHAIN);
//先在元数据里放上chain
while (iterator.hasNext()) {
JsonNode innerJsonObject = iterator.next();
chainList.forEach(e -> {
//校验加载的 chainName 是否有重复的
// TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
//String chainName = innerJsonObject.get(NAME).textValue();
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
//TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
}
});
});
// 清空
chainNameSet.clear();
}
public static void parseChainDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer){
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
documentList.forEach(document -> {
// 解析chain节点
List<Element> chainList = document.getRootElement().elements(CHAIN);
//先在元数据里放上chain
chainList.forEach(e -> {
//校验加载的 chainName 是否有重复的
//TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
});
});
// 清空
chainNameSet.clear();
//解析每一个chain
for (Document document : documentList) {
Element rootElement = document.getRootElement();
List<Element> chainList = rootElement.elements(CHAIN);
chainList.forEach(parseOneChainConsumer);
}
}
public static void parseNodeJson(List<JsonNode> flowJsonObjectList) {
for (JsonNode flowJsonNode : flowJsonObjectList) {
// 当存在<nodes>节点定义时解析node节点
if (flowJsonNode.get(FLOW).has(NODES)) {
@@ -201,7 +206,61 @@ public class ParserHelper {
ParserHelper.buildNode(nodePropBean);
}
}
}
}
public static void parseJsonNode(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer) {
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
//先在元数据里放上chain
while (iterator.hasNext()) {
JsonNode innerJsonObject = iterator.next();
//校验加载的 chainName 是否有重复的
// TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
//String chainName = innerJsonObject.get(NAME).textValue();
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
}
});
// 清空
chainNameSet.clear();
}
public static void parseChainJson(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer){
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
//先在元数据里放上chain
while (iterator.hasNext()) {
JsonNode innerJsonObject = iterator.next();
//校验加载的 chainName 是否有重复的
// TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
}
});
// 清空
chainNameSet.clear();
for (JsonNode flowJsonNode : flowJsonObjectList) {
//解析每一个chain
Iterator<JsonNode> chainIterator = flowJsonNode.get(FLOW).get(CHAIN).elements();
while (chainIterator.hasNext()) {
@@ -218,9 +277,9 @@ public class ParserHelper {
*/
public static void parseOneChainEl(JsonNode chainNode) {
//构建chainBuilder
String chainName = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
String el = chainNode.get(VALUE).textValue();
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
chainELBuilder.setEL(el).build();
}
@@ -231,10 +290,10 @@ public class ParserHelper {
*/
public static void parseOneChainEl(Element e) {
//构建chainBuilder
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
String text = e.getText();
String el = RegexUtil.removeComments(text);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
chainELBuilder.setEL(el).build();
}

View File

@@ -12,4 +12,4 @@
language (groovy|js) #IMPLIED
>
<!ATTLIST chain
name CDATA #REQUIRED>
name CDATA>