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}