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