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