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.parser;
017
018import com.alibaba.fastjson.JSON;
019import com.alibaba.fastjson.JSONArray;
020import com.alibaba.fastjson.JSONObject;
021import dev.tinyflow.core.chain.ChainDefinition;
022import dev.tinyflow.core.chain.Edge;
023import dev.tinyflow.core.chain.JsCodeCondition;
024import dev.tinyflow.core.chain.Node;
025import dev.tinyflow.core.util.CollectionUtil;
026import dev.tinyflow.core.util.StringUtil;
027
028import java.util.HashMap;
029import java.util.Map;
030
031public class ChainParser {
032
033    private final Map<String, NodeParser<?>> nodeParserMap;
034
035    public ChainParser(Map<String, NodeParser<?>> nodeParserMap) {
036        this.nodeParserMap = nodeParserMap;
037    }
038
039    public Map<String, NodeParser<?>> getNodeParserMap() {
040        return nodeParserMap;
041    }
042
043    public void addNodeParser(String type, NodeParser<?> nodeParser) {
044        this.nodeParserMap.put(type, nodeParser);
045    }
046
047    public void removeNodeParser(String type) {
048        this.nodeParserMap.remove(type);
049    }
050
051
052    public ChainDefinition parse(String jsonString) {
053        if (StringUtil.noText(jsonString)) {
054            throw new IllegalStateException("jsonString is empty");
055        }
056
057        JSONObject root = JSON.parseObject(jsonString);
058        JSONArray nodes = root.getJSONArray("nodes");
059        JSONArray edges = root.getJSONArray("edges");
060
061        return parse(root, nodes, edges);
062    }
063
064
065    public ChainDefinition parse(JSONObject chainJSONObject, JSONArray nodes, JSONArray edges) {
066        if (CollectionUtil.noItems(nodes) || CollectionUtil.noItems(edges)) {
067            return null;
068        }
069
070        ChainDefinition definition = new ChainDefinition();
071        for (int i = 0; i < nodes.size(); i++) {
072            JSONObject nodeObject = nodes.getJSONObject(i);
073//            if ((parentNode == null && StringUtil.noText(nodeObject.getString("parentId")))
074//                    || (parentNode != null && parentNode.getString("id").equals(nodeObject.getString("parentId")))) {
075                Node node = parseNode(chainJSONObject, nodeObject);
076                if (node != null) {
077                    definition.addNode(node);
078                }
079//            }
080        }
081
082        for (int i = 0; i < edges.size(); i++) {
083//            JSONObject edgeObject = edges.getJSONObject(i);
084//            JSONObject edgeData = edgeObject.getJSONObject("data");
085//            if ((parentNode == null && (edgeData == null || StringUtil.noText(edgeData.getString("parentNodeId"))))
086//                    || (parentNode != null && edgeData != null && edgeData.getString("parentNodeId").equals(parentNode.getString("id"))
087//                    //不添加子流程里的第一条 edge(也就是父节点连接子节点的第一条线)
088//                    && !parentNode.getString("id").equals(edgeObject.getString("source")))) {
089//                ChainEdge edge = parseEdge(edgeObject);
090//                if (edge != null) {
091//                    chain.addEdge(edge);
092//                }
093//            }
094
095            JSONObject edgeObject = edges.getJSONObject(i);
096            Edge edge = parseEdge(edgeObject);
097            if (edge == null) {
098                continue;
099            }
100//            if (parentNode == null ||
101//                    //不添加子流程里的第一条 edge(也就是父节点连接子节点的第一条线)
102//                    (!parentNode.getString("id").equals(edgeObject.getString("source")))
103//            ) {
104                definition.addEdge(edge);
105//            }
106        }
107
108        return definition;
109    }
110
111    private Node parseNode(JSONObject chainJSONObject, JSONObject nodeObject) {
112        String type = nodeObject.getString("type");
113        if (StringUtil.noText(type)) {
114            return null;
115        }
116
117        NodeParser<?> nodeParser = nodeParserMap.get(type);
118        return nodeParser == null ? null : nodeParser.parse(nodeObject, chainJSONObject, this);
119    }
120
121
122    private Edge parseEdge(JSONObject edgeObject) {
123        if (edgeObject == null) return null;
124        Edge edge = new Edge();
125        edge.setId(edgeObject.getString("id"));
126        edge.setSource(edgeObject.getString("source"));
127        edge.setTarget(edgeObject.getString("target"));
128
129        JSONObject data = edgeObject.getJSONObject("data");
130        if (data == null || data.isEmpty()) {
131            return edge;
132        }
133
134        String conditionString = data.getString("condition");
135        if (StringUtil.hasText(conditionString)) {
136            edge.setCondition(new JsCodeCondition(conditionString.trim()));
137        }
138        return edge;
139    }
140
141    public void addAllParsers(Map<String, NodeParser<?>> defaultNodeParsers) {
142        this.nodeParserMap.putAll(defaultNodeParsers);
143    }
144    
145
146    public static Builder builder() {
147        return new Builder();
148    }
149
150
151    public static final class Builder {
152
153        private final Map<String, NodeParser<?>> customParsers = new HashMap<>();
154        private boolean includeDefaults = true;
155
156        private Builder() {
157        }
158
159        /**
160         * 是否包含默认节点解析器(默认为 true)。
161         */
162        public Builder withDefaultParsers(boolean include) {
163            this.includeDefaults = include;
164            return this;
165        }
166
167        /**
168         * 添加自定义节点解析器(会覆盖同名的默认解析器)。
169         */
170        public Builder addParser(String type, NodeParser<?> parser) {
171            if (type == null || parser == null) {
172                throw new IllegalArgumentException("type and parser must not be null");
173            }
174            this.customParsers.put(type, parser);
175            return this;
176        }
177
178        /**
179         * 批量添加自定义解析器。
180         */
181        public Builder addParsers(Map<String, NodeParser<?>> parsers) {
182            if (parsers != null) {
183                this.customParsers.putAll(parsers);
184            }
185            return this;
186        }
187
188        public ChainParser build() {
189            Map<String, NodeParser<?>> finalMap = new HashMap<>();
190
191            if (includeDefaults) {
192                finalMap.putAll(DefaultNodeParsers.getDefaultNodeParsers());
193            }
194
195            // 自定义解析器覆盖默认
196            finalMap.putAll(customParsers);
197
198            return new ChainParser(finalMap);
199        }
200    }
201}