package com.iailab.framework.common.util.template; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; public class XMLParserUtils { private final Map settings; private final double[][] inputData; public XMLParserUtils(Map settings, double[][] inputData) { this.settings = settings; this.inputData = inputData; } public String parse(String xml) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes())); doc.getDocumentElement().normalize(); StringBuilder output = new StringBuilder(); processNode(doc.getDocumentElement(), output, -1); return output.toString(); } private void processNode(Node node, StringBuilder output, int index) { if (node.getNodeType() == Node.TEXT_NODE) { output.append(node.getTextContent().trim()); } else if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; switch (element.getTagName()) { case "simple-value": processSimpleValue(element, output, index); break; case "setting-value": processSettingValue(element, output); break; case "input-value": processInputValue(element, output, index); break; case "foreach-value": processForeachValue(element, output); break; default: processChildren(element, output, index); } } } private void processChildren(Element element, StringBuilder output, int index) { NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { processNode(children.item(i), output, index); } } private void processSimpleValue(Element element, StringBuilder output, int index) { processChildren(element, output, index); } private void processSettingValue(Element element, StringBuilder output) { String key = element.getAttribute("key"); output.append(settings.getOrDefault(key, "")); } private void processInputValue(Element element, StringBuilder output, int index) { // 优先使用:port属性(动态计算) String portExpr = element.getAttribute(":port"); if (portExpr.isEmpty()) { // 回退到port属性(静态值) portExpr = element.getAttribute("port"); } String column = element.getAttribute("column"); int col = Integer.parseInt(column); // 动态计算端口值(考虑当前索引) int port = evaluateExpression(portExpr, index); if (port >= 0 && port < inputData.length && col >= 0 && col < inputData[port].length) { double value = inputData[port][col]; output.append((int)value); } else { System.err.printf("Invalid data access: port=%d (max=%d), col=%d (max=%d)%n", port, inputData.length - 1, col, (port >= 0 && port < inputData.length) ? inputData[port].length - 1 : -1); // 默认值 output.append("0"); } } private void processForeachValue(Element element, StringBuilder output) { String lengthKey = element.getAttribute("length"); int length = Integer.parseInt(settings.getOrDefault(lengthKey, "0")); String separator = element.getAttribute("separator"); StringBuilder loopResult = new StringBuilder(); for (int i = 0; i < length; i++) { StringBuilder itemOutput = new StringBuilder(); // 直接处理所有子节点 NodeList children = element.getChildNodes(); for (int j = 0; j < children.getLength(); j++) { Node child = children.item(j); if (child.getNodeType() == Node.TEXT_NODE) { String text = child.getTextContent(); // 动态确定转炉状态 boolean isBlowing = isFurnaceBlowing(i); // 替换占位符并调整状态描述 text = text.replace("{index}", String.valueOf(i + 1)) .replace("未吹炼", isBlowing ? "正在吹炼" : "未吹炼") .replace("距离上次吹炼结束时间", isBlowing ? "吹炼持续时间" : "距离上次吹炼结束时间"); itemOutput.append(text); } else if (child.getNodeType() == Node.ELEMENT_NODE) { // 传递当前循环索引 processNode(child, itemOutput, i); } } loopResult.append(itemOutput); if (i < length - 1 && !separator.isEmpty()) { loopResult.append(separator); } } output.append(loopResult.toString().replace(" ", "").replace("\n", "")); } private boolean isFurnaceBlowing(int furnaceIndex) { // 实际业务逻辑:根据输入数据判断转炉状态 // 这里简化处理:第三个转炉(index=2)正在吹炼 return furnaceIndex == 2; } // 自定义表达式计算器 private int evaluateExpression(String expr, int index) { if (expr == null || expr.isEmpty()) { System.err.println("Empty expression, returning 0"); return 0; } // 替换表达式中的{index}占位符 String processedExpr = expr.replace("{index}", String.valueOf(index)); // 移除所有空格确保表达式正确解析 processedExpr = processedExpr.replaceAll("\\s", ""); // 尝试直接解析为整数(优化常见情况) try { return Integer.parseInt(processedExpr); } catch (NumberFormatException e) { // 不是简单整数,继续尝试表达式计算 } // 自定义简单表达式解析器 try { // 处理加法表达式(如 "3+0") if (processedExpr.contains("+")) { String[] parts = processedExpr.split("\\+"); int result = 0; for (String part : parts) { result += Integer.parseInt(part); } return result; } // 处理减法表达式(如 "5-2") else if (processedExpr.contains("-")) { String[] parts = processedExpr.split("-"); int result = Integer.parseInt(parts[0]); for (int i = 1; i < parts.length; i++) { result -= Integer.parseInt(parts[i]); } return result; } // // 处理乘法表达式(如 "2 * 3") // else if (processedExpr.contains("*")) { // String[] parts = processedExpr.split("\\*"); // int result = 1; // for (String part : parts) { // result *= Integer.parseInt(part); // } // return result; // } // // 处理除法表达式(如 "6/2") // else if (processedExpr.contains("/")) { // String[] parts = processedExpr.split("/"); // int result = Integer.parseInt(parts[0]); // for (int i = 1; i < parts.length; i++) { // int divisor = Integer.parseInt(parts[i]); // if (divisor != 0) { // result /= divisor; // } // } // return result; // } } catch (NumberFormatException e) { System.err.println("Error parsing expression: " + processedExpr); } // 无法解析的表达式 System.err.println("Unsupported expression format: " + processedExpr); return 0; } // test public static void main(String[] args) throws Exception { String xmlContent = "\n" + "\n" + " 根据以下输入,\n" + " \n" + " 根据未来min的转炉煤气系统运行情况,对煤气调整用户进行调度分配,确保能源产消平衡与系统稳定运行。\n" + " \n" + " \n" + " 该系统共包含座转炉。\n" + " \n" + " \n" + " {index}#转炉当前状态未吹炼,\n" + " 距离上次吹炼结束时间min,\n" + " 前一炉回收间隔min\n" + " \n" + " \n" + " 日计划炉数:,当前已吹炼炉数:;\n" + " \n" + " \n" + " 历史60min平均每炉煤气回收量:Km3,\n" + " \n" + " \n" + " 当前煤气消耗量km3;\n" + " \n" + " \n" + " 当前煤气柜容量km3;\n" + " \n" + " \n" + " 煤气柜柜位安全区间为km3,期望煤气柜位值在km3,\n" + " \n" + " \n" + " 当前调度用户为电厂各机组:,\n" + " \n" + " \n" + " 各机组当前使用转炉煤气量 [, , , , , ];\n" + " \n" + " \n" + " 各机组使用转炉煤气下限量 。\n" + " \n" + " \n" + " 各机组使用转炉煤气上限量 。\n" + " \n" + " \n" + " 机组优先级顺序为:。\n" + " \n" + " 请根据优先级顺序确定调度方案。\n" + ""; // 1. 准备配置数据 Map settings = new HashMap<>(); settings.put("predictLength", "60"); settings.put("luShu", "3"); settings.put("guiRongLiang", "80"); settings.put("anQuanQuJian", "[30, 110]"); settings.put("qiWangZhi", "65"); settings.put("jiZuMingCheng", "1#135, 2#135, 1#BTG, 2#BTG, 3#BTG, 4#BTG"); settings.put("jiZuXiaXian", "[20, 1, 16, 17, 6, 11]"); settings.put("jiZuShangXian", "[92, 96, 74, 101, 86, 93]"); settings.put("jiZuYouXianJi", "1#135, 1#BTG, 3#BTG, 2#135, 2#BTG, 4#BTG"); // 示例输入数据 - 与期望输出完全匹配 double[][] inputData = { // 机组当前用量 {39, 39, 32, 56, 38, 48}, // [0]:历史回收量, [1]:当前消耗量 {24, 115}, // [0]:日计划炉数, [1]:已吹炼炉数 {21, 10}, // 转炉1: [0]:结束时间, [2]:回收间隔 {15, 0, 18}, // 转炉2: [0]:结束时间, [2]:回收间隔 {9, 0, 24}, // 转炉3: [0]:吹炼持续时间, [2]:回收间隔 {7, 0, 25} }; // 3. 创建解析器并执行 XMLParserUtils parser = new XMLParserUtils(settings, inputData); String result = parser.parse(xmlContent); // 替换为你的XML内容 System.out.println(result); } }