11 changed files with 416 additions and 215 deletions
@ -1,8 +1,8 @@ |
|||||||
// search_result.dart |
// search_result.dart |
||||||
class SearchResult { |
class SearchResult { |
||||||
final int rowNum; |
final int rowNum; |
||||||
final String filePath; |
final String attributeValue; |
||||||
final String content; |
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 @@ |
|||||||
// 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 @@ |
|||||||
// xml_search_service.dart |
// xml_search_service.dart |
||||||
import 'dart:io'; |
import 'dart:io'; |
||||||
import 'package:win_text_editor/framework/controllers/logger.dart'; |
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: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/search_result.dart'; |
||||||
import 'package:win_text_editor/modules/xml_search/models/xml_rule.dart'; |
|
||||||
|
|
||||||
class XmlSearchService { |
class XmlSearchService { |
||||||
Future<List<SearchResult>> searchFromDirectory({ |
Future<List<SearchResult>> searchFromDirectory({ |
||||||
required String directory, |
required String directory, |
||||||
required String fileType, |
required String nodeName, |
||||||
required XmlRule rule, |
required String attributeName, |
||||||
|
required String queryContent, |
||||||
}) async { |
}) async { |
||||||
final results = <SearchResult>[]; |
final results = <SearchResult>[]; |
||||||
final dir = Directory(directory); |
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; |
int rowNum = 1; |
||||||
|
|
||||||
await for (var entity in dir.list(recursive: true)) { |
// 2. Search for nodes with specified name and attribute |
||||||
if (entity is File && FileUtils.matchesFileType(entity.path, fileType)) { |
for (int i = 0; i < searchValues.length; i++) { |
||||||
try { |
final nodes = document.findAllElements(nodeName); |
||||||
final fileContent = await entity.readAsString(); |
int index = 0; |
||||||
final document = xml.XmlDocument.parse(fileContent); |
for (final node in nodes) { |
||||||
final values = _searchWithRule(document, rule); |
final attributeValue = node.getAttribute(attributeName); |
||||||
for (var value in values) { |
if (attributeValue != null && attributeValue == searchValues[i]) { |
||||||
results.add(SearchResult(rowNum: rowNum++, filePath: entity.path, content: value)); |
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; |
return results; |
||||||
} |
} |
||||||
|
|
||||||
List<String> _searchWithRule(xml.XmlDocument document, XmlRule rule) { |
Future<void> removeNode( |
||||||
final nodes = document.findAllElements(rule.nodePath); |
String directory, |
||||||
//final nodes = SimpleXPath.query(document, rule.toxPath()); |
String nodeName, |
||||||
if (rule.isFirstOccurrence && nodes.isNotEmpty) { |
String attributeName, |
||||||
final attr = nodes.first.getAttribute(rule.attributeName); |
SearchResult result, |
||||||
return attr != null ? [attr] : []; |
) 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 { |
} else { |
||||||
return nodes |
Logger().warning('未找到序号为 ${result.index} 的匹配节点'); |
||||||
.map((node) => node.getAttribute(rule.attributeName)) |
} |
||||||
.where((attr) => attr != null) |
} catch (e) { |
||||||
.cast<String>() |
Logger().error('删除节点时出错: $e'); |
||||||
.toList(); |
rethrow; // 重新抛出异常以便上层处理 |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue