diff --git a/src/main/java/com/example/demo/draft/demo043/core/TemplateFileLoader.java b/src/main/java/com/example/demo/draft/demo043/core/TemplateFileLoader.java index fb65135..4580ad7 100644 --- a/src/main/java/com/example/demo/draft/demo043/core/TemplateFileLoader.java +++ b/src/main/java/com/example/demo/draft/demo043/core/TemplateFileLoader.java @@ -12,39 +12,97 @@ import java.util.Map; /** * 模板文件工具类 - * 负责从文件系统或类路径加载模板文件 + * 负责从文件系统或类路径加载模板文件,支持缓存机制提高性能 + * + *

该类提供了以下功能:

+ * + * + *

使用示例:

+ *
+ * // 从类路径加载
+ * String template1 = TemplateFileLoader.loadTemplate("classpath:templates/default.html");
+ * // 从文件系统加载
+ * String template2 = TemplateFileLoader.loadTemplate("file:/opt/templates/default.html");
+ * // 默认从类路径加载
+ * String template3 = TemplateFileLoader.loadTemplate("templates/default.html");
+ * 
+ * + * @author 岳佳君 + * @version 1.0 */ @Slf4j public class TemplateFileLoader { + /** + * 默认模板目录路径 + */ private static final String DEFAULT_TEMPLATE_DIR = "templates/"; + + /** + * 类路径前缀标识符 + */ private static final String CLASS_PATH_PREFIX = "classpath:"; + + /** + * 文件系统路径前缀标识符 + */ private static final String FILE_PATH_PREFIX = "file:"; - // 模板缓存 + /** + * 模板缓存,使用HashMap存储已加载的模板内容 + * Key: 缓存键(包含路径前缀和模板路径) + * Value: 模板文件内容 + */ private static final Map TEMPLATE_CACHE = new HashMap<>(); /** * 从类路径加载模板文件 + * + *

该方法会首先检查缓存中是否已存在该模板,如果存在则直接返回缓存内容, + * 否则从类路径读取文件内容并存入缓存。

+ * + * @param templatePath 模板文件路径,可以是相对路径或绝对路径 + * (相对路径相对于类路径根目录) + * @return 模板文件的内容字符串 + * @throws IOException 当模板文件未找到或读取失败时抛出 + * @throws IllegalArgumentException 当templatePath为null或空字符串时抛出 */ public static String loadTemplateFromClasspath(String templatePath) throws IOException { + // 参数校验 + if (templatePath == null || templatePath.trim().isEmpty()) { + throw new IllegalArgumentException("模板路径不能为空"); + } + String cacheKey = CLASS_PATH_PREFIX + templatePath; + + // 检查缓存中是否已存在该模板 if (TEMPLATE_CACHE.containsKey(cacheKey)) { + log.debug("从缓存加载类路径模板: {}", templatePath); return TEMPLATE_CACHE.get(cacheKey); } try { - // 从类路径加载 + // 处理路径格式,确保以"/"开头 String fullPath = templatePath.startsWith("/") ? templatePath : "/" + templatePath; java.io.InputStream inputStream = TemplateFileLoader.class.getResourceAsStream(fullPath); + // 检查文件是否存在 if (inputStream == null) { throw new IOException("模板文件未找到: " + templatePath); } + // 读取文件内容并转换为字符串 String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + + // 存入缓存 TEMPLATE_CACHE.put(cacheKey, content); - log.info("从类路径加载模板: {}", templatePath); + log.info("从类路径加载模板成功: {}", templatePath); return content; } catch (IOException e) { @@ -55,22 +113,43 @@ public class TemplateFileLoader { /** * 从文件系统加载模板文件 + * + *

该方法会首先检查缓存中是否已存在该模板,如果存在则直接返回缓存内容, + * 否则从文件系统读取文件内容并存入缓存。

+ * + * @param filePath 模板文件的绝对路径或相对路径(相对于当前工作目录) + * @return 模板文件的内容字符串 + * @throws IOException 当模板文件不存在或读取失败时抛出 + * @throws IllegalArgumentException 当filePath为null或空字符串时抛出 */ public static String loadTemplateFromFileSystem(String filePath) throws IOException { + // 参数校验 + if (filePath == null || filePath.trim().isEmpty()) { + throw new IllegalArgumentException("文件路径不能为空"); + } + String cacheKey = FILE_PATH_PREFIX + filePath; + + // 检查缓存中是否已存在该模板 if (TEMPLATE_CACHE.containsKey(cacheKey)) { + log.debug("从缓存加载文件系统模板: {}", filePath); return TEMPLATE_CACHE.get(cacheKey); } try { Path path = Paths.get(filePath); + + // 检查文件是否存在 if (!Files.exists(path)) { throw new IOException("模板文件不存在: " + filePath); } + // 读取文件内容 String content = Files.readString(path, StandardCharsets.UTF_8); + + // 存入缓存 TEMPLATE_CACHE.put(cacheKey, content); - log.info("从文件系统加载模板: {}", filePath); + log.info("从文件系统加载模板成功: {}", filePath); return content; } catch (IOException e) { @@ -80,12 +159,31 @@ public class TemplateFileLoader { } /** - * 智能加载模板(自动判断路径类型) + * 智能加载模板文件(自动判断路径类型) + * + *

根据路径前缀自动选择加载方式:

+ * + * + * @param templatePath 模板路径,支持带前缀的完整路径或不带前缀的相对路径 + * @return 模板文件的内容字符串 + * @throws IOException 当模板文件加载失败时抛出 + * @throws IllegalArgumentException 当templatePath为null或空字符串时抛出 */ public static String loadTemplate(String templatePath) throws IOException { + // 参数校验 + if (templatePath == null || templatePath.trim().isEmpty()) { + throw new IllegalArgumentException("模板路径不能为空"); + } + if (templatePath.startsWith(CLASS_PATH_PREFIX)) { + // 从类路径加载,去掉前缀 return loadTemplateFromClasspath(templatePath.substring(CLASS_PATH_PREFIX.length())); } else if (templatePath.startsWith(FILE_PATH_PREFIX)) { + // 从文件系统加载,去掉前缀 return loadTemplateFromFileSystem(templatePath.substring(FILE_PATH_PREFIX.length())); } else { // 默认从类路径加载 @@ -94,17 +192,33 @@ public class TemplateFileLoader { } /** - * 清除模板缓存 + * 清除所有模板缓存 + * + *

该方法会清空当前内存中所有的模板缓存,适用于需要强制重新加载模板的场景, + * 比如模板文件内容已更新但缓存还未过期的情况。

*/ public static void clearCache() { + int cacheSize = TEMPLATE_CACHE.size(); TEMPLATE_CACHE.clear(); - log.info("模板缓存已清除"); + log.info("模板缓存已清除,原缓存大小: {}", cacheSize); } /** - * 获取缓存中的模板数量 + * 获取当前缓存中的模板数量 + * + * @return 缓存中模板的数量 */ public static int getCacheSize() { return TEMPLATE_CACHE.size(); } -} + + /** + * 检查指定路径的模板是否已缓存 + * + * @param templatePath 模板路径(需要包含完整的前缀) + * @return 如果模板已缓存返回true,否则返回false + */ + public static boolean isCached(String templatePath) { + return TEMPLATE_CACHE.containsKey(templatePath); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/draft/demo043/core/Xml2AFSIMTransformation.java b/src/main/java/com/example/demo/draft/demo043/core/Xml2AFSIMTransformation.java index f7a4324..8739f78 100644 --- a/src/main/java/com/example/demo/draft/demo043/core/Xml2AFSIMTransformation.java +++ b/src/main/java/com/example/demo/draft/demo043/core/Xml2AFSIMTransformation.java @@ -13,34 +13,87 @@ import java.util.Map; import java.util.Stack; import java.util.regex.Pattern; +/** + * XML转AFSIM格式转换器 + * + *

该类提供将XML文件转换为JSON格式并应用正则表达式规则进行键名转换的功能。 + * 采用流式处理方式,适用于处理大型XML文件,避免内存溢出。

+ * + *

主要功能:

+ * + * + * @author 岳佳君 + * @version 1.0 + */ @Slf4j public class Xml2AFSIMTransformation { + /** + * 正则表达式规则映射 + * Key: 编译后的正则表达式模式 + * Value: 替换字符串 + */ private final Map regexRules; + /** + * 构造函数,初始化转换规则 + * + * @param rules 转换规则映射,Key为匹配的正则表达式,Value为替换字符串 + * 如果为null,则使用空规则集 + */ public Xml2AFSIMTransformation(Map rules) { - log.info("初始化转换器,规则数量: {}", rules != null ? rules.size() : 0); + log.info("初始化XML转AFSIM转换器,规则数量: {}", rules != null ? rules.size() : 0); // 编译正则表达式规则 regexRules = new HashMap<>(); if (rules != null) { for (Map.Entry entry : rules.entrySet()) { + // 预编译正则表达式以提高性能 regexRules.put(Pattern.compile(entry.getKey()), entry.getValue()); } } + log.debug("正则表达式规则编译完成"); } /** - * 流式XML转JSON并应用转换规则 + * 将XML文件转换为JSON格式并应用转换规则 + * + *

该方法使用StAX流式API解析XML,适用于处理大型文件。 + * 转换后的JSON格式为对象数组,每个XML根元素对应一个JSON对象。

+ * + * @param xmlFile 输入的XML文件,不能为null + * @param jsonFile 输出的JSON文件,不能为null + * @param rootElement 根元素名称,用于标识每个JSON对象的开始和结束 + * @throws Exception 当文件读写失败、XML解析错误或转换过程中出现异常时抛出 + * @throws IllegalArgumentException 当参数为null或空字符串时抛出 */ public void convertXmlToJson(File xmlFile, File jsonFile, String rootElement) throws Exception { + // 参数校验 + if (xmlFile == null || !xmlFile.exists()) { + throw new IllegalArgumentException("XML文件不存在或为null: " + xmlFile); + } + if (jsonFile == null) { + throw new IllegalArgumentException("JSON文件路径不能为null"); + } + if (rootElement == null || rootElement.trim().isEmpty()) { + throw new IllegalArgumentException("根元素名称不能为空"); + } + log.info("开始XML转JSON转换并应用规则"); + log.debug("输入文件: {}, 输出文件: {}, 根元素: {}", xmlFile.getPath(), jsonFile.getPath(), rootElement); try (FileInputStream xmlInputStream = new FileInputStream(xmlFile); FileWriter jsonWriter = new FileWriter(jsonFile)) { + // 创建XML流读取器 XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(xmlInputStream); + // 写入JSON数组开始标记 jsonWriter.write("[\n"); boolean firstObject = true; @@ -52,6 +105,7 @@ public class Xml2AFSIMTransformation { StringBuilder currentText = new StringBuilder(); String currentElement = null; + // 流式解析XML while (reader.hasNext()) { int event = reader.next(); @@ -80,7 +134,7 @@ public class Xml2AFSIMTransformation { String text = reader.getText().trim(); if (!text.isEmpty()) { currentText.append(text); - log.debug("文本内容: {}", text); + log.trace("文本内容: {}", text); } } break; @@ -112,6 +166,7 @@ public class Xml2AFSIMTransformation { jsonWriter.write(json); objectCount++; + // 每处理100个对象刷新一次缓冲区并记录日志 if (objectCount % 100 == 0) { log.info("已处理 {} 个对象", objectCount); jsonWriter.flush(); @@ -125,48 +180,68 @@ public class Xml2AFSIMTransformation { } } + // 写入JSON数组结束标记 jsonWriter.write("\n]"); log.info("转换完成,共处理 {} 个对象", objectCount); } catch (Exception e) { - log.error("转换失败", e); + log.error("XML转JSON转换失败,文件: {}", xmlFile.getPath(), e); throw e; } } /** - * 处理元素的属性 + * 处理XML元素的属性 + * + *

将元素的属性添加到当前对象中,属性键名为"元素名@属性名"格式

+ * + * @param reader XML流读取器 + * @param elementStack 元素栈,用于跟踪当前解析路径 + * @param currentObject 当前正在构建的对象 */ private void processAttributesForElement(XMLStreamReader reader, Stack elementStack, Map currentObject) { - if (reader.getAttributeCount() > 0) { - for (int i = 0; i < reader.getAttributeCount(); i++) { + int attributeCount = reader.getAttributeCount(); + if (attributeCount > 0) { + log.debug("处理元素 {} 的属性,属性数量: {}", elementStack.peek(), attributeCount); + for (int i = 0; i < attributeCount; i++) { String attrName = reader.getAttributeLocalName(i); String attrValue = reader.getAttributeValue(i); - // 默认处理:将属性添加到对象中 + // 将属性添加到对象中,格式为:元素名@属性名 String attrKey = elementStack.peek() + "@" + attrName; currentObject.put(attrKey, attrValue); + log.trace("添加属性: {} = {}", attrKey, attrValue); } } } /** - * 处理元素文本内容 + * 处理XML元素的文本内容 + * + *

将元素的文本内容添加到当前对象中,键名为完整的元素路径

+ * + * @param elementName 元素名称 + * @param content 元素文本内容 + * @param elementStack 元素栈,用于构建完整路径 + * @param currentObject 当前正在构建的对象 */ private void processElementContent(String elementName, String content, Stack elementStack, Map currentObject) { - // 默认处理:将元素内容添加到对象中 - // 构建完整的元素路径作为key + // 构建完整的元素路径作为key:父元素.当前元素 String elementKey = String.join(".", elementStack) + "." + elementName; currentObject.put(elementKey, content); + log.debug("添加元素内容: {} = {}", elementKey, content); } /** - * 将对象转换为JSON字符串 + * 将Map对象转换为JSON字符串 + * + * @param object 要转换的对象映射 + * @return JSON格式的字符串,如果对象为空则返回null */ private String convertObjectToJson(Map object) { - if (object.isEmpty()) { - log.warn("空对象,跳过输出"); + if (object == null || object.isEmpty()) { + log.warn("空对象,跳过JSON转换"); return null; } @@ -174,12 +249,15 @@ public class Xml2AFSIMTransformation { return mapToJson(object); } catch (Exception e) { log.error("对象转JSON失败", e); - return "{}"; + return "{}"; // 返回空对象作为容错处理 } } /** - * Map转JSON字符串 + * 递归将Map转换为JSON字符串 + * + * @param map 要转换的映射 + * @return JSON对象字符串 */ private String mapToJson(Map map) { if (map.isEmpty()) { @@ -204,7 +282,10 @@ public class Xml2AFSIMTransformation { } /** - * 值转JSON + * 将值转换为JSON格式 + * + * @param value 要转换的值,支持String、Number、Boolean、Map、List等类型 + * @return JSON格式的值表示 */ private String valueToJson(Object value) { if (value == null) { @@ -239,14 +320,21 @@ public class Xml2AFSIMTransformation { return json.toString(); } + // 默认处理:转换为字符串 return "\"" + escapeJson(value.toString()) + "\""; } /** - * JSON特殊字符转义 + * JSON字符串转义处理 + * + *

转义JSON中的特殊字符,包括引号、反斜杠、控制字符等

+ * + * @param text 要转义的文本 + * @return 转义后的安全JSON字符串 */ private String escapeJson(String text) { if (text == null) return ""; + return text.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\b", "\\b") @@ -258,6 +346,11 @@ public class Xml2AFSIMTransformation { /** * 应用正则表达式转换规则到单个对象 + * + *

遍历对象的所有键,对每个键应用正则表达式规则进行转换

+ * + * @param originalMap 原始对象映射 + * @return 应用规则转换后的新对象映射 */ private Map applyRules(Map originalMap) { Map transformedMap = new HashMap<>(); @@ -270,25 +363,32 @@ public class Xml2AFSIMTransformation { // 如果找到匹配的规则则使用新key,否则保留原key if (newKey != null) { transformedMap.put(newKey, value); - log.debug("转换键: {} -> {}", originalKey, newKey); + log.debug("键名转换: {} -> {}", originalKey, newKey); } else { transformedMap.put(originalKey, value); } } + log.debug("规则应用完成,原始键数: {}, 转换后键数: {}", originalMap.size(), transformedMap.size()); return transformedMap; } /** * 查找并替换匹配的正则规则 + * + *

遍历所有正则规则,找到第一个匹配的规则并进行替换

+ * + * @param originalKey 原始键名 + * @return 替换后的新键名,如果没有匹配规则则返回null */ private String findAndReplace(String originalKey) { for (Map.Entry rule : regexRules.entrySet()) { if (rule.getKey().matcher(originalKey).matches()) { - return rule.getKey().matcher(originalKey).replaceAll(rule.getValue()); + String newKey = rule.getKey().matcher(originalKey).replaceAll(rule.getValue()); + log.trace("正则匹配: {} -> {} (规则: {})", originalKey, newKey, rule.getKey().pattern()); + return newKey; } } return null; } - -} +} \ No newline at end of file