001/** 002 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.gnu.org/licenses/lgpl-3.0.txt 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package dev.tinyflow.core.node; 017 018import com.agentsflex.core.chain.Chain; 019import com.agentsflex.core.chain.ChainStatus; 020import com.agentsflex.core.chain.Parameter; 021import com.agentsflex.core.chain.RefType; 022import com.agentsflex.core.chain.node.BaseNode; 023 024import java.util.*; 025 026public class LoopNode extends BaseNode { 027 028 private Parameter loopVar; 029 private Chain loopChain; 030 031 public Parameter getLoopVar() { 032 return loopVar; 033 } 034 035 public void setLoopVar(Parameter loopVar) { 036 this.loopVar = loopVar; 037 } 038 039 public Chain getLoopChain() { 040 return loopChain; 041 } 042 043 public void setLoopChain(Chain loopChain) { 044 this.loopChain = loopChain; 045 } 046 047 @Override 048 protected Map<String, Object> execute(Chain chain) { 049 loopChain.setStatus(ChainStatus.READY); 050 051 // 复制父流程的参数 052 loopChain.setEventListeners(chain.getEventListeners()); 053 loopChain.setOutputListeners(chain.getOutputListeners()); 054 loopChain.setChainErrorListeners(chain.getChainErrorListeners()); 055 loopChain.setNodeErrorListeners(chain.getNodeErrorListeners()); 056 loopChain.setSuspendNodes(chain.getSuspendNodes()); 057 058 059 Map<String, Object> executeResult = new HashMap<>(); 060 Map<String, Object> chainMemory = chain.getMemory().getAll(); 061 062 Map<String, Object> loopVars = chain.getParameterValues(this, Collections.singletonList(loopVar)); 063 Object loopValue = loopVars.get(loopVar.getName()); 064 if (loopValue instanceof Iterable) { 065 Iterable<?> iterable = (Iterable<?>) loopValue; 066 int index = 0; 067 for (Object o : iterable) { 068 if (this.loopChain.getStatus() != ChainStatus.READY) { 069 break; 070 } 071 executeLoopChain(index++, o, chainMemory, executeResult); 072 } 073 } else if (loopValue instanceof Number || (loopValue instanceof String && isNumeric(loopValue.toString()))) { 074 int count = loopValue instanceof Number ? ((Number) loopValue).intValue() : Integer.parseInt(loopValue.toString().trim()); 075 for (int i = 0; i < count; i++) { 076 if (this.loopChain.getStatus() != ChainStatus.READY) { 077 break; 078 } 079 executeLoopChain(i, i, chainMemory, executeResult); 080 } 081 } 082 083 return executeResult; 084 } 085 086 private void executeLoopChain(int index, Object loopItem, Map<String, Object> parentMap, Map<String, Object> executeResult) { 087 Map<String, Object> loopParams = new HashMap<>(); 088 loopParams.put(this.id + ".index", index); 089 loopParams.put(this.id + ".loopItem", loopItem); 090 loopParams.putAll(parentMap); 091 try { 092 loopChain.execute(loopParams); 093 } finally { 094 // 正常结束的情况下,填充结果 095 if (loopChain.getStatus() == ChainStatus.FINISHED_NORMAL) { 096 fillResult(executeResult, loopChain); 097 098 //重置 chain statue 为 ready 099 loopChain.reset(); 100 } 101 } 102 } 103 104 /** 105 * 判断字符串是否是数字 106 * 107 * @param string 需要判断的字符串 108 * @return boolean 是数字返回 true,否则返回 false 109 */ 110 private boolean isNumeric(String string) { 111 if (string == null || string.isEmpty()) { 112 return false; 113 } 114 char[] chars = string.trim().toCharArray(); 115 for (char c : chars) { 116 if (!Character.isDigit(c)) { 117 return false; 118 } 119 } 120 return true; 121 } 122 123 /** 124 * 把子流程执行的结果填充到主流程的输出参数中 125 * 126 * @param executeResult 主流程的输出参数 127 * @param loopChain 子流程的 128 */ 129 private void fillResult(Map<String, Object> executeResult, Chain loopChain) { 130 List<Parameter> outputDefs = getOutputDefs(); 131 if (outputDefs != null) { 132 for (Parameter outputDef : outputDefs) { 133 Object value = null; 134 135 //引用 136 if (outputDef.getRefType() == RefType.REF) { 137 value = loopChain.get(outputDef.getRef()); 138 } 139 //固定值 140 else if (outputDef.getRefType() == RefType.FIXED) { 141 value = outputDef.getValue(); 142 } 143 144 @SuppressWarnings("unchecked") List<Object> existList = (List<Object>) executeResult.get(outputDef.getName()); 145 if (existList == null) { 146 existList = new ArrayList<>(); 147 } 148 existList.add(value); 149 executeResult.put(outputDef.getName(), existList); 150 } 151 } 152 } 153}