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.Parameter; 020import com.agentsflex.core.chain.RefType; 021import com.agentsflex.core.chain.node.BaseNode; 022 023import java.util.*; 024 025public class LoopNode extends BaseNode { 026 027 private Parameter loopVar; 028 private Chain loopChain; 029 030 public Parameter getLoopVar() { 031 return loopVar; 032 } 033 034 public void setLoopVar(Parameter loopVar) { 035 this.loopVar = loopVar; 036 } 037 038 public Chain getLoopChain() { 039 return loopChain; 040 } 041 042 public void setLoopChain(Chain loopChain) { 043 this.loopChain = loopChain; 044 } 045 046 @Override 047 protected Map<String, Object> execute(Chain chain) { 048 loopChain.setParent(chain); 049 050 Map<String, Object> executeResult = new HashMap<>(); 051 Map<String, Object> chainMemory = chain.getMemory().getAll(); 052 053 Map<String, Object> loopVars = chain.getParameterValues(this, Collections.singletonList(loopVar)); 054 Object loopValue = loopVars.get(loopVar.getName()); 055 if (loopValue instanceof Iterable) { 056 Iterable<?> iterable = (Iterable<?>) loopValue; 057 int index = 0; 058 for (Object o : iterable) { 059 executeLoopChain(index++, o, loopVars, chainMemory, executeResult); 060 } 061 } else if (loopValue instanceof Number || (loopValue instanceof String && isNumeric(loopValue.toString()))) { 062 int count = loopValue instanceof Number ? ((Number) loopValue).intValue() : Integer.parseInt(loopValue.toString().trim()); 063 for (int i = 0; i < count; i++) { 064 executeLoopChain(i, i, loopVars, chainMemory, executeResult); 065 } 066 } 067 068 return executeResult; 069 } 070 071 private void executeLoopChain(int index, Object loopItem, Map<String, Object> loopVars, Map<String, Object> parentMap, Map<String, Object> executeResult) { 072 Map<String, Object> loopParams = new HashMap<>(); 073 loopParams.put("index", index); 074 loopParams.put("loopItem", loopItem); 075 loopParams.putAll(loopVars); 076 loopParams.putAll(parentMap); 077 try { 078 loopChain.execute(loopParams); 079 } finally { 080 fillResult(executeResult, loopChain); 081 loopChain.reset(); 082 } 083 } 084 085 /** 086 * 判断字符串是否是数字 087 * 088 * @param string 需要判断的字符串 089 * @return boolean 是数字返回 true,否则返回 false 090 */ 091 private boolean isNumeric(String string) { 092 if (string == null || string.isEmpty()) { 093 return false; 094 } 095 char[] chars = string.trim().toCharArray(); 096 for (char c : chars) { 097 if (!Character.isDigit(c)) { 098 return false; 099 } 100 } 101 return true; 102 } 103 104 /** 105 * 把子流程执行的结果填充到主流程的输出参数中 106 * 107 * @param executeResult 主流程的输出参数 108 * @param loopChain 子流程的 109 */ 110 private void fillResult(Map<String, Object> executeResult, Chain loopChain) { 111 List<Parameter> outputDefs = getOutputDefs(); 112 if (outputDefs != null) { 113 for (Parameter outputDef : outputDefs) { 114 Object value = null; 115 116 //引用 117 if (outputDef.getRefType() == RefType.REF) { 118 value = loopChain.get(outputDef.getRef()); 119 } 120 //固定值 121 else if (outputDef.getRefType() == RefType.FIXED) { 122 value = outputDef.getValue(); 123 } 124 125 @SuppressWarnings("unchecked") List<Object> existList = (List<Object>) executeResult.get(outputDef.getName()); 126 if (existList == null) { 127 existList = new ArrayList<>(); 128 } 129 existList.add(value); 130 executeResult.put(outputDef.getName(), existList); 131 } 132 } 133 } 134}