11 changed files with 416 additions and 215 deletions
@ -1,8 +1,8 @@
@@ -1,8 +1,8 @@
|
||||
// search_result.dart |
||||
class SearchResult { |
||||
final int rowNum; |
||||
final String filePath; |
||||
final String content; |
||||
final String attributeValue; |
||||
int index = 0; |
||||
|
||||
SearchResult({required this.rowNum, required this.filePath, required this.content}); |
||||
SearchResult({required this.rowNum, required this.attributeValue, this.index = 0}); |
||||
} |
||||
|
@ -1,18 +0,0 @@
@@ -1,18 +0,0 @@
|
||||
// xml_rule.dart |
||||
class XmlRule { |
||||
final String nodePath; |
||||
final String attributeName; |
||||
final bool isFirstOccurrence; |
||||
final String? namespacePrefix; |
||||
|
||||
XmlRule({ |
||||
required this.nodePath, |
||||
required this.attributeName, |
||||
this.isFirstOccurrence = false, |
||||
this.namespacePrefix, |
||||
}); |
||||
|
||||
String toxPath() { |
||||
return '${namespacePrefix != null ? '$namespacePrefix:' : ''}$nodePath${isFirstOccurrence ? '[0]' : ''}/${attributeName.isNotEmpty ? '@$attributeName' : 'text()'}'; |
||||
} |
||||
} |
@ -1,52 +1,94 @@
@@ -1,52 +1,94 @@
|
||||
// xml_search_service.dart |
||||
import 'dart:io'; |
||||
import 'package:win_text_editor/framework/controllers/logger.dart'; |
||||
import 'package:win_text_editor/shared/utils/file_utils.dart'; |
||||
import 'package:xml/xml.dart' as xml; |
||||
import 'package:win_text_editor/modules/xml_search/models/search_result.dart'; |
||||
import 'package:win_text_editor/modules/xml_search/models/xml_rule.dart'; |
||||
|
||||
class XmlSearchService { |
||||
Future<List<SearchResult>> searchFromDirectory({ |
||||
required String directory, |
||||
required String fileType, |
||||
required XmlRule rule, |
||||
required String nodeName, |
||||
required String attributeName, |
||||
required String queryContent, |
||||
}) async { |
||||
final results = <SearchResult>[]; |
||||
final dir = Directory(directory); |
||||
int rowNum = 1; |
||||
|
||||
await for (var entity in dir.list(recursive: true)) { |
||||
if (entity is File && FileUtils.matchesFileType(entity.path, fileType)) { |
||||
try { |
||||
final fileContent = await entity.readAsString(); |
||||
final document = xml.XmlDocument.parse(fileContent); |
||||
final values = _searchWithRule(document, rule); |
||||
for (var value in values) { |
||||
results.add(SearchResult(rowNum: rowNum++, filePath: entity.path, content: value)); |
||||
try { |
||||
final searchValues = |
||||
queryContent |
||||
.split(RegExp(r'[\n,]')) |
||||
.map((e) => e.trim()) |
||||
.where((e) => e.isNotEmpty) |
||||
.toList(); |
||||
if (searchValues.isEmpty) { |
||||
searchValues.add(queryContent); |
||||
} |
||||
|
||||
final file = File(directory); |
||||
final content = await file.readAsString(); |
||||
final document = xml.XmlDocument.parse(content); |
||||
|
||||
int rowNum = 1; |
||||
|
||||
// 2. Search for nodes with specified name and attribute |
||||
for (int i = 0; i < searchValues.length; i++) { |
||||
final nodes = document.findAllElements(nodeName); |
||||
int index = 0; |
||||
for (final node in nodes) { |
||||
final attributeValue = node.getAttribute(attributeName); |
||||
if (attributeValue != null && attributeValue == searchValues[i]) { |
||||
results.add( |
||||
SearchResult(rowNum: rowNum++, attributeValue: attributeValue, index: index++), |
||||
); |
||||
} |
||||
} catch (e) { |
||||
Logger().error('xmlSearchService.searchFromDirectory方法执行出错: $e'); |
||||
results.add(SearchResult(rowNum: rowNum++, filePath: entity.path, content: 'Error: $e')); |
||||
} |
||||
} |
||||
Logger().info("共发现记录 ${rowNum - 1} 条"); |
||||
} catch (e) { |
||||
Logger().error('xmlSearchService.searchFromDirectory方法执行出错: $e'); |
||||
} |
||||
|
||||
return results; |
||||
} |
||||
|
||||
List<String> _searchWithRule(xml.XmlDocument document, XmlRule rule) { |
||||
final nodes = document.findAllElements(rule.nodePath); |
||||
//final nodes = SimpleXPath.query(document, rule.toxPath()); |
||||
if (rule.isFirstOccurrence && nodes.isNotEmpty) { |
||||
final attr = nodes.first.getAttribute(rule.attributeName); |
||||
return attr != null ? [attr] : []; |
||||
} else { |
||||
return nodes |
||||
.map((node) => node.getAttribute(rule.attributeName)) |
||||
.where((attr) => attr != null) |
||||
.cast<String>() |
||||
.toList(); |
||||
Future<void> removeNode( |
||||
String directory, |
||||
String nodeName, |
||||
String attributeName, |
||||
SearchResult result, |
||||
) async { |
||||
try { |
||||
// 1. 打开并解析 XML 文件 |
||||
final file = File(directory); |
||||
final content = await file.readAsString(); |
||||
final document = xml.XmlDocument.parse(content); |
||||
|
||||
// 2. 查找所有匹配的节点 |
||||
final nodes = |
||||
document.findAllElements(nodeName).where((node) { |
||||
final attributeValue = node.getAttribute(attributeName); |
||||
return attributeValue == result.attributeValue; |
||||
}).toList(); |
||||
|
||||
// 3. 检查是否存在指定序号的节点 |
||||
if (result.index >= 0 && result.index < nodes.length) { |
||||
final nodeToRemove = nodes[result.index]; |
||||
|
||||
// 4. 删除节点 |
||||
nodeToRemove.parent?.children.remove(nodeToRemove); |
||||
|
||||
// 5. 保存修改后的 XML 文件 |
||||
final newContent = document.toXmlString(pretty: true); |
||||
await file.writeAsString(newContent); |
||||
|
||||
Logger().info( |
||||
'成功删除节点: $nodeName[$attributeName="${result.attributeValue}"][${result.index}]', |
||||
); |
||||
} else { |
||||
Logger().warning('未找到序号为 ${result.index} 的匹配节点'); |
||||
} |
||||
} catch (e) { |
||||
Logger().error('删除节点时出错: $e'); |
||||
rethrow; // 重新抛出异常以便上层处理 |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue