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 loopChain.setComputeCost(0L); 058 059 060 Map<String, Object> executeResult = new HashMap<>(); 061 Map<String, Object> chainMemory = chain.getMemory().getAll(); 062 063 Map<String, Object> loopVars = chain.getParameterValues(this, Collections.singletonList(loopVar)); 064 Object loopValue = loopVars.get(loopVar.getName()); 065 if (loopValue instanceof Iterable) { 066 Iterable<?> iterable = (Iterable<?>) loopValue; 067 int index = 0; 068 for (Object o : iterable) { 069 if (this.loopChain.getStatus() != ChainStatus.READY) { 070 break; 071 } 072 executeLoopChain(index++, o, chainMemory, executeResult); 073 } 074 } else if (loopValue instanceof Number || (loopValue instanceof String && isNumeric(loopValue.toString()))) { 075 int count = loopValue instanceof Number ? ((Number) loopValue).intValue() : Integer.parseInt(loopValue.toString().trim()); 076 for (int i = 0; i < count; i++) { 077 if (this.loopChain.getStatus() != ChainStatus.READY) { 078 break; 079 } 080 executeLoopChain(i, i, chainMemory, executeResult); 081 } 082 } 083 084 return executeResult; 085 } 086 087 private void executeLoopChain(int index, Object loopItem, Map<String, Object> parentMap, Map<String, Object> executeResult) { 088 Map<String, Object> loopParams = new HashMap<>(); 089 loopParams.put(this.id + ".index", index); 090 loopParams.put(this.id + ".loopItem", loopItem); 091 loopParams.putAll(parentMap); 092 try { 093 loopChain.execute(loopParams); 094 this.addComputeCost(loopChain.getComputeCost()); 095 } finally { 096 // 正常结束的情况下,填充结果 097 if (loopChain.getStatus() == ChainStatus.FINISHED_NORMAL) { 098 fillResult(executeResult, loopChain); 099 100 //重置 chain statue 为 ready 101 loopChain.reset(); 102 } 103 } 104 } 105 106 /** 107 * 判断字符串是否是数字 108 * 109 * @param string 需要判断的字符串 110 * @return boolean 是数字返回 true,否则返回 false 111 */ 112 private boolean isNumeric(String string) { 113 if (string == null || string.isEmpty()) { 114 return false; 115 } 116 char[] chars = string.trim().toCharArray(); 117 for (char c : chars) { 118 if (!Character.isDigit(c)) { 119 return false; 120 } 121 } 122 return true; 123 } 124 125 /** 126 * 把子流程执行的结果填充到主流程的输出参数中 127 * 128 * @param executeResult 主流程的输出参数 129 * @param loopChain 子流程的 130 */ 131 private void fillResult(Map<String, Object> executeResult, Chain loopChain) { 132 List<Parameter> outputDefs = getOutputDefs(); 133 if (outputDefs != null) { 134 for (Parameter outputDef : outputDefs) { 135 Object value = null; 136 137 //引用 138 if (outputDef.getRefType() == RefType.REF) { 139 value = loopChain.get(outputDef.getRef()); 140 } 141 //固定值 142 else if (outputDef.getRefType() == RefType.FIXED) { 143 value = outputDef.getValue(); 144 } 145 146 @SuppressWarnings("unchecked") List<Object> existList = (List<Object>) executeResult.get(outputDef.getName()); 147 if (existList == null) { 148 existList = new ArrayList<>(); 149 } 150 existList.add(value); 151 executeResult.put(outputDef.getName(), existList); 152 } 153 } 154 } 155}