feature #I9T6PB 嵌套循环获得任意外层的下标或者对象

This commit is contained in:
everywhere.z
2024-08-28 23:41:36 +08:00
parent ba9710a83d
commit 8c051a4290
10 changed files with 218 additions and 14 deletions

View File

@@ -10,7 +10,6 @@ package com.yomahub.liteflow.core;
import cn.hutool.core.date.StopWatch; import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil; import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
import com.yomahub.liteflow.enums.CmpStepTypeEnum; import com.yomahub.liteflow.enums.CmpStepTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum;
@@ -419,10 +418,26 @@ public abstract class NodeComponent{
return this.getRefNode().getLoopIndex(); return this.getRefNode().getLoopIndex();
} }
public Integer getPreLoopIndex() {
return this.getRefNode().getPreLoopIndex();
}
public Integer getPreNLoopIndex(int n) {
return this.getRefNode().getPreNLoopIndex(n);
}
public <T> T getCurrLoopObj() { public <T> T getCurrLoopObj() {
return this.getRefNode().getCurrLoopObject(); return this.getRefNode().getCurrLoopObject();
} }
public <T> T getPreLoopObj() {
return this.getRefNode().getPreLoopObject();
}
public <T> T getPreNLoopObj(int n) {
return this.getRefNode().getPreNLoopObject(n);
}
@Deprecated @Deprecated
public void invoke(String chainId, Object param) throws Exception { public void invoke(String chainId, Object param) throws Exception {
FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex()); FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());

View File

@@ -17,12 +17,16 @@ import com.yomahub.liteflow.enums.ExecuteableTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ChainEndException; import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.exception.FlowSystemException; import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
import com.yomahub.liteflow.flow.executor.NodeExecutor; import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; import com.yomahub.liteflow.flow.executor.NodeExecutorHelper;
import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot; import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.TupleOf2;
import java.util.Stack;
/** /**
* Node节点实现可执行器 Node节点并不是单例的每构建一次都会copy出一个新的实例 * Node节点实现可执行器 Node节点并不是单例的每构建一次都会copy出一个新的实例
@@ -60,10 +64,10 @@ public class Node implements Executable, Cloneable, Rollbackable{
private TransmittableThreadLocal<Boolean> accessResult = new TransmittableThreadLocal<>(); private TransmittableThreadLocal<Boolean> accessResult = new TransmittableThreadLocal<>();
// 循环下标 // 循环下标
private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>(); private TransmittableThreadLocal<Stack<TupleOf2<Integer, Integer>>> loopIndexTL = new TransmittableThreadLocal<>();
// 迭代对象 // 迭代对象
private TransmittableThreadLocal<Object> currLoopObject = new TransmittableThreadLocal<>(); private TransmittableThreadLocal<Stack<TupleOf2<Integer, Object>>> loopObjectTL = new TransmittableThreadLocal<>();
// 当前slot的index // 当前slot的index
private TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>(); private TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>();
@@ -290,28 +294,106 @@ public class Node implements Executable, Cloneable, Rollbackable{
this.isContinueOnErrorResult.remove(); this.isContinueOnErrorResult.remove();
} }
public void setLoopIndex(int index) { public void setLoopIndex(LoopCondition condition, int index) {
this.loopIndexTL.set(index); if (this.loopIndexTL.get() == null){
Stack<TupleOf2<Integer, Integer>> stack = new Stack<>();
TupleOf2<Integer, Integer> tuple = new TupleOf2<>(condition.hashCode(), index);
stack.push(tuple);
this.loopIndexTL.set(stack);
}else{
Stack<TupleOf2<Integer, Integer>> stack = this.loopIndexTL.get();
TupleOf2<Integer, Integer> thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null);
if (thisConditionTuple != null){
thisConditionTuple.setB(index);
}else{
TupleOf2<Integer, Integer> tuple = new TupleOf2<>(condition.hashCode(), index);
stack.push(tuple);
}
}
} }
public Integer getLoopIndex() { public Integer getLoopIndex() {
return this.loopIndexTL.get(); Stack<TupleOf2<Integer, Integer>> stack = this.loopIndexTL.get();
if (stack != null){
return stack.peek().getB();
}else{
return null;
}
}
public Integer getPreLoopIndex(){
return getPreNLoopIndex(1);
}
public Integer getPreNLoopIndex(int n){
Stack<TupleOf2<Integer, Integer>> stack = this.loopIndexTL.get();
if (stack != null && stack.size() > n){
return stack.elementAt(stack.size() - (n + 1)).getB();
}else{
return null;
}
} }
public void removeLoopIndex() { public void removeLoopIndex() {
this.loopIndexTL.remove(); Stack<TupleOf2<Integer, Integer>> stack = this.loopIndexTL.get();
if (stack != null){
if (stack.size() > 1){
stack.pop();
}else{
this.loopIndexTL.remove();
}
}
} }
public void setCurrLoopObject(Object obj) { public void setCurrLoopObject(LoopCondition condition, Object obj) {
this.currLoopObject.set(obj); if (this.loopObjectTL.get() == null){
Stack<TupleOf2<Integer, Object>> stack = new Stack<>();
TupleOf2<Integer, Object> tuple = new TupleOf2<>(condition.hashCode(), obj);
stack.push(tuple);
this.loopObjectTL.set(stack);
}else{
Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
TupleOf2<Integer, Object> thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null);
if (thisConditionTuple != null){
thisConditionTuple.setB(obj);
}else{
TupleOf2<Integer, Object> tuple = new TupleOf2<>(condition.hashCode(), obj);
stack.push(tuple);
}
}
} }
public <T> T getCurrLoopObject() { public <T> T getCurrLoopObject() {
return (T) this.currLoopObject.get(); Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
if (stack != null){
return (T) stack.peek().getB();
}else{
return null;
}
}
public <T> T getPreLoopObject(){
return getPreNLoopObject(1);
}
public <T> T getPreNLoopObject(int n){
Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
if (stack != null && stack.size() > n){
return (T) stack.elementAt(stack.size() - (n + 1)).getB();
}else{
return null;
}
} }
public void removeCurrLoopObject() { public void removeCurrLoopObject() {
this.currLoopObject.remove(); Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
if (stack != null){
if (stack.size() > 1){
stack.pop();
}else{
this.loopObjectTL.remove();
}
}
} }
public Integer getSlotIndex(){ public Integer getSlotIndex(){
@@ -355,7 +437,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
public Node clone() throws CloneNotSupportedException { public Node clone() throws CloneNotSupportedException {
Node node = (Node)super.clone(); Node node = (Node)super.clone();
node.loopIndexTL = new TransmittableThreadLocal<>(); node.loopIndexTL = new TransmittableThreadLocal<>();
node.currLoopObject = new TransmittableThreadLocal<>(); node.loopObjectTL = new TransmittableThreadLocal<>();
node.accessResult = new TransmittableThreadLocal<>(); node.accessResult = new TransmittableThreadLocal<>();
node.slotIndexTL = new TransmittableThreadLocal<>(); node.slotIndexTL = new TransmittableThreadLocal<>();
node.isEndTL = new TransmittableThreadLocal<>(); node.isEndTL = new TransmittableThreadLocal<>();

View File

@@ -43,7 +43,7 @@ public abstract class LoopCondition extends Condition {
((Condition) executableItem).getExecutableGroup() ((Condition) executableItem).getExecutableGroup()
.forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index))); .forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index)));
} else if (executableItem instanceof Node) { } else if (executableItem instanceof Node) {
((Node) executableItem).setLoopIndex(index); ((Node) executableItem).setLoopIndex(this, index);
} }
} }
@@ -54,7 +54,7 @@ public abstract class LoopCondition extends Condition {
((Condition) executableItem).getExecutableGroup() ((Condition) executableItem).getExecutableGroup()
.forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj))); .forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj)));
} else if (executableItem instanceof Node) { } else if (executableItem instanceof Node) {
((Node) executableItem).setCurrLoopObject(obj); ((Node) executableItem).setCurrLoopObject(this, obj);
} }
} }

View File

@@ -0,0 +1,29 @@
package com.yomahub.liteflow.util;
public class TupleOf2<A, B> {
private A a;
private B b;
public TupleOf2(A a, B b) {
this.a = a;
this.b = b;
}
public A getA() {
return a;
}
public B getB() {
return b;
}
public void setA(A a) {
this.a = a;
}
public void setB(B b) {
this.b = b;
}
}

View File

@@ -56,4 +56,16 @@ public class IteratorELSpringbootTest extends BaseTest {
LiteflowResponse response = flowExecutor.execute2Resp("chain3"); LiteflowResponse response = flowExecutor.execute2Resp("chain3");
Assertions.assertTrue(response.isSuccess()); Assertions.assertTrue(response.isSuccess());
} }
//多层迭代循环取各层obj
@Test
public void testIt4() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4");
DefaultContext context = response.getFirstContextBean();
String indexStr = context.getData("index_str");
String objStr = context.getData("obj_str");
System.out.println(indexStr);
System.out.println(objStr);
Assertions.assertTrue(response.isSuccess());
}
} }

View File

@@ -0,0 +1,34 @@
package com.yomahub.liteflow.test.iterator.cmp;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DefaultContext;
import org.springframework.stereotype.Component;
@Component("e")
public class ECmp extends NodeComponent {
@Override
public void process() throws Exception {
DefaultContext context = this.getFirstContextBean();
if(context.hasData("index_str")){
String indexStr = context.getData("index_str").toString();
indexStr = StrUtil.format("{}[{}{}]", indexStr, this.getPreLoopIndex(), this.getLoopIndex());
context.setData("index_str", indexStr);
}else{
context.setData("index_str", StrUtil.format("[{}{}]", this.getPreLoopIndex(), this.getLoopIndex()));
}
if(context.hasData("obj_str")){
String objStr = context.getData("obj_str").toString();
objStr = StrUtil.format("{}[{}{}]", objStr, this.getPreLoopObj(), this.getCurrLoopObj());
context.setData("obj_str", objStr);
}else{
context.setData("obj_str", StrUtil.format("[{}{}]", this.getPreLoopObj().toString(), this.getCurrLoopObj().toString()));
}
System.out.println("ECmp executed!");
}
}

View File

@@ -108,4 +108,14 @@ public class LoopELSpringbootTest extends BaseTest {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertTrue(response.isSuccess()); Assertions.assertTrue(response.isSuccess());
} }
//FOR循环多层嵌套获取下标
@Test
public void testLoop10() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
DefaultContext context = response.getFirstContextBean();
String assertStr = context.getData("index_str").toString();
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("[000][001][010][011][020][021][100][101][110][111][120][121][200][201][210][211][220][221][300][301][310][311][320][321]", assertStr);
}
} }

View File

@@ -7,7 +7,9 @@
*/ */
package com.yomahub.liteflow.test.loop.cmp; package com.yomahub.liteflow.test.loop.cmp;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DefaultContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component("c") @Component("c")
@@ -15,6 +17,16 @@ public class CCmp extends NodeComponent {
@Override @Override
public void process() { public void process() {
DefaultContext context = this.getFirstContextBean();
if(context.hasData("index_str")){
String indexStr = context.getData("index_str").toString();
indexStr = StrUtil.format("{}[{}{}{}]", indexStr, this.getPreNLoopIndex(2), this.getPreLoopIndex(), this.getLoopIndex());
context.setData("index_str", indexStr);
}else{
context.setData("index_str", StrUtil.format("[{}{}{}]", this.getPreNLoopIndex(2), this.getPreLoopIndex(), this.getLoopIndex()));
}
System.out.println("CCmp executed!"); System.out.println("CCmp executed!");
} }

View File

@@ -18,4 +18,10 @@
); );
</chain> </chain>
<chain name="chain4">
ITERATOR(x1).DO(
ITERATOR(x2).DO(e)
);
</chain>
</flow> </flow>

View File

@@ -52,4 +52,8 @@
<chain name="chain9"> <chain name="chain9">
FOR(2).DO(THEN(c, c, c)); FOR(2).DO(THEN(c, c, c));
</chain> </chain>
<chain name="chain10">
FOR(4).DO(FOR(3).DO(FOR(2).DO(c)));
</chain>
</flow> </flow>