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<String, String> settings;
|
private final double[][] inputData;
|
|
public XMLParserUtils(Map<String, String> 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
"<iquestion>\n" +
|
" <simple-value>根据以下输入,</simple-value>\n" +
|
" <simple-value>\n" +
|
" 根据未来<setting-value key=\"predictLength\"/>min的转炉煤气系统运行情况,对煤气调整用户进行调度分配,确保能源产消平衡与系统稳定运行。\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 该系统共包含<setting-value key=\"luShu\"/>座转炉。\n" +
|
" </simple-value>\n" +
|
" <foreach-value length=\"luShu\" index=\"index\" separator=\";\">\n" +
|
" {index}#转炉当前状态未吹炼,\n" +
|
" 距离上次吹炼结束时间<input-value :port=\"3 + {index}\" column=\"0\" type=\"double\"/>min,\n" +
|
" 前一炉回收间隔<input-value :port=\"3 + {index}\" column=\"2\" type=\"double\"/>min\n" +
|
" </foreach-value>\n" +
|
" <simple-value>\n" +
|
" 日计划炉数:<input-value port=\"2\" column=\"0\" type=\"double\"/>,当前已吹炼炉数:<input-value port=\"3\" column=\"0\" type=\"double\"/>;\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 历史60min平均每炉煤气回收量:<input-value port=\"1\" column=\"0\" type=\"double\"/>Km3,\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 当前煤气消耗量<input-value port=\"1\" column=\"1\" type=\"double\"/>km3;\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 当前煤气柜容量<setting-value key=\"guiRongLiang\"/>km3;\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 煤气柜柜位安全区间为<setting-value key=\"anQuanQuJian\"/>km3,期望煤气柜位值在<setting-value key=\"qiWangZhi\"/>km3,\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 当前调度用户为电厂各机组:<setting-value key=\"jiZuMingCheng\"/>,\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 各机组当前使用转炉煤气量 [<input-value port=\"0\" column=\"0\" type=\"double\"/>, <input-value port=\"0\" column=\"1\" type=\"double\"/>, <input-value port=\"0\" column=\"2\" type=\"double\"/>, <input-value port=\"0\" column=\"3\" type=\"double\"/>, <input-value port=\"0\" column=\"4\" type=\"double\"/>, <input-value port=\"0\" column=\"5\" type=\"double\"/>];\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 各机组使用转炉煤气下限量 <setting-value key=\"jiZuXiaXian\"/>。\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 各机组使用转炉煤气上限量 <setting-value key=\"jiZuShangXian\"/>。\n" +
|
" </simple-value>\n" +
|
" <simple-value>\n" +
|
" 机组优先级顺序为:<setting-value key=\"jiZuYouXianJi\"/>。\n" +
|
" </simple-value>\n" +
|
" <simple-value>请根据优先级顺序确定调度方案。</simple-value>\n" +
|
"</iquestion>";
|
|
// 1. 准备配置数据
|
Map<String, String> 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);
|
}
|
}
|