#ifndef XMLTOOL_H
#define XMLTOOL_H
#include <QString>
#include <QMap>
#include <QMutex>
#include <QWaitCondition>
#include <pugixml.hpp>
#include <QFile>
#include <QTextStream>
class XmlTool {
public:
static bool beginTransaction(const QString& fileName);
static bool commitTransaction(const QString& fileName);
static bool rollbackTransaction(const QString& fileName);
static bool loadXmlStream(const QString& fileName, pugi::xml_document& doc);
static bool saveXmlStream(const QString& fileName, const pugi::xml_document& doc);
static bool addNode(const QString& fileName, const QString& parentXPath, const QString& nodeName, const QMap<QString, QString>& attributes);
static bool removeNode(const QString& fileName, const QString& xpath);
static bool updateNode(const QString& fileName, const QString& xpath, const QMap<QString, QString>& attributes);
static QList<pugi::xml_node> query(const QString& fileName, const QString& xpath);
static QList<pugi::xml_node> queryAndSort(const QString& fileName, const QString& xpath, const QList<QPair<QString, bool>>& sortRules);
static void add_indentation(pugi::xml_node node, int indent_level, std::ostringstream &oss);
static void process_if_node(pugi::xml_node if_node, pugi::xml_node &parent_node);
static void process_for_node(pugi::xml_node for_node, pugi::xml_node &parent_node);
static void process_all_if_nodes(pugi::xml_node node);
static void process_all_for_nodes(pugi::xml_node node);
static int docFrameContent(std::string input_path, std::string output_path);
private:
static pugi::xml_document* loadXml(const QString& fileName);
static void ensureDocumentLoaded(const QString& fileName);
static bool processXmlChunk(const QString& chunk, pugi::xml_document& doc);
static QString xmlDocumentToString(const pugi::xml_document& doc);
static void ensureLock(const QString& fileName);
static void waitForUnlock(const QString& fileName);
static void releaseLock(const QString& fileName);
static bool hasAttribute(const pugi::xml_node& node, const QString &attributeName);
static void setNodeAttribute(pugi::xml_node& newNode,QString attrName,QString attrValue);
static QList<pugi::xml_node> sortNodesByRules(const QList<pugi::xml_node>& nodes, const QList<QPair<QString, bool>>& sortRules);
static void sortNodesByTagName(pugi::xml_node& parent);
static void sortAttributesByAttributeName(pugi::xml_node& node);
static void sortNodeRecursively(pugi::xml_node& node);
static QMap<QString, pugi::xml_document*> xmlDocs_;
static QMap<QString, QMutex*> fileLocks_;
static QMap<QString, QWaitCondition*> fileConditions_;
static QMutex globalMutex_;
static QMap<QString, bool> transactionInProgress_;
static QMap<QString, QString> transactionBackups_;
};
#endif
#include "XmlTool.h"
#include <QDebug>
#include <sstream>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
QMap<QString, pugi::xml_document*> XmlTool::xmlDocs_;
QMap<QString, QMutex*> XmlTool::fileLocks_;
QMap<QString, QWaitCondition*> XmlTool::fileConditions_;
QMutex XmlTool::globalMutex_;
QMap<QString, bool> XmlTool::transactionInProgress_;
QMap<QString, QString> XmlTool::transactionBackups_;
bool XmlTool::beginTransaction(const QString& fileName) {
QMutexLocker globalLocker(&globalMutex_);
ensureLock(fileName);
if (!transactionInProgress_.contains(fileName)) {
transactionBackups_[fileName] = xmlDocs_.contains(fileName) ? xmlDocumentToString(*xmlDocs_[fileName]) : "";
transactionInProgress_[fileName] = true;
return true;
}
return false;
}
bool XmlTool::commitTransaction(const QString& fileName) {
QMutexLocker globalLocker(&globalMutex_);
ensureLock(fileName);
if (transactionInProgress_.contains(fileName)) {
transactionInProgress_.remove(fileName);
transactionBackups_.remove(fileName);
return true;
}
return false;
}
bool XmlTool::rollbackTransaction(const QString& fileName) {
QMutexLocker globalLocker(&globalMutex_);
ensureLock(fileName);
if (transactionInProgress_.contains(fileName)) {
if (transactionBackups_.contains(fileName)) {
QString backupData = transactionBackups_[fileName];
delete xmlDocs_[fileName];
xmlDocs_[fileName] = new pugi::xml_document();
std::istringstream iss(backupData.toStdString());
xmlDocs_[fileName]->load(iss);
}
transactionInProgress_.remove(fileName);
transactionBackups_.remove(fileName);
return saveXmlStream(fileName, *xmlDocs_[fileName]);
}
return false;
}
bool XmlTool::loadXmlStream(const QString& fileName, pugi::xml_document& doc) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Failed to open XML file for reading:" << fileName;
return false;
}
QByteArray xmlData = file.readAll();
file.close();
std::string xmlString = xmlData.toStdString();
qWarning()<<"xml content:"<<xmlString.c_str();
pugi::xml_parse_result result = doc.load_buffer(xmlString.c_str(), xmlString.size(), pugi::parse_default);
if (!result) {
qWarning() << "Failed to parse XML file:" << fileName;
return false;
}
return true;
}
bool XmlTool::saveXmlStream(const QString& fileName, const pugi::xml_document& doc) {
pugi::xml_node root = doc.child("root");
std::ostringstream oss;
oss << "<?xml version=\"1.0\"?>";
add_indentation(root, 0, oss);
std::ofstream output(fileName.toStdString().c_str());
if (!output) {
std::cerr << "Failed to open the file " << fileName.toStdString().c_str() << std::endl;
return 1;
}
output << oss.str();
std::cout << "XML updated and formatted successfully." << std::endl;
return true;
}
void XmlTool::setNodeAttribute(pugi::xml_node& newNode,QString attrName,QString attrValue)
{
if(hasAttribute(newNode,attrName)){
newNode.attribute(attrName.toStdString().c_str()) = attrValue.toStdString().c_str();
}else{
newNode.append_attribute(attrName.toStdString().c_str()) = attrValue.toStdString().c_str();
}
}
bool XmlTool::addNode(const QString& fileName, const QString& parentXPath, const QString& nodeName, const QMap<QString, QString>& attributes) {
if (!beginTransaction(fileName)) {
return false;
}
pugi::xml_document* doc = loadXml(fileName);
try {
pugi::xml_node parent = doc->select_node(parentXPath.toStdString().c_str()).node();
if (parent) {
pugi::xml_node newNode = parent.append_child(nodeName.toStdString().c_str());
if (!newNode) {
qWarning() << "Failed to append child node:" << nodeName;
rollbackTransaction(fileName);
return false;
}
for (auto attrIt = attributes.begin(); attrIt != attributes.end(); ++attrIt) {
setNodeAttribute(newNode,attrIt.key(),attrIt.value());
}
sortNodesByTagName(parent);
sortAttributesByAttributeName(newNode);
if (commitTransaction(fileName)) {
return saveXmlStream(fileName, *doc);
} else {
rollbackTransaction(fileName);
qWarning() << "Failed to commit transaction after adding node.";
return false;
}
} else {
qWarning() << "Parent node not found for XPath:" << parentXPath;
}
rollbackTransaction(fileName);
} catch (...) {
rollbackTransaction(fileName);
}
return false;
}
bool XmlTool::removeNode(const QString& fileName, const QString& xpath) {
if (!beginTransaction(fileName)) {
return false;
}
pugi::xml_document* doc = loadXml(fileName);
try {
QList<pugi::xml_node> nodes = query(fileName,xpath);
if(nodes.size()>0){
for(pugi::xml_node node:nodes){
pugi::xml_node parent = node.parent();
parent.remove_child(node);
}
if (commitTransaction(fileName)) {
return saveXmlStream(fileName, *doc);
} else {
rollbackTransaction(fileName);
qWarning() << "Failed to commit transaction after removing node.";
return false;
}
} else {
qWarning() << "Node not found for XPath:" << xpath;
}
rollbackTransaction(fileName);
} catch (...) {
rollbackTransaction(fileName);
}
return false;
}
bool XmlTool::updateNode(const QString& fileName, const QString& xpath, const QMap<QString, QString>& attributes) {
if (!beginTransaction(fileName)) {
return false;
}
pugi::xml_document* doc = loadXml(fileName);
try {
pugi::xml_node node = doc->select_node(xpath.toStdString().c_str()).node();
if (node) {
pugi::xml_attribute attr = node.first_attribute();
while (attr) {
pugi::xml_attribute nextAttr = attr.next_attribute();
node.remove_attribute(attr.name());
attr = nextAttr;
}
for (auto attrIt = attributes.begin(); attrIt != attributes.end(); ++attrIt) {
setNodeAttribute(node,attrIt.key(),attrIt.value());
}
sortAttributesByAttributeName(node);
if (commitTransaction(fileName)) {
return saveXmlStream(fileName, *doc);
} else {
rollbackTransaction(fileName);
qWarning() << "Failed to commit transaction after updating node.";
return false;
}
} else {
qWarning() << "Node not found for XPath:" << xpath;
}
rollbackTransaction(fileName);
} catch (...) {
rollbackTransaction(fileName);
}
return false;
}
QList<pugi::xml_node> XmlTool::query(const QString& fileName, const QString& xpath) {
QList<pugi::xml_node> result;
QMutexLocker globalLocker(&globalMutex_);
ensureLock(fileName);
pugi::xml_document* doc = loadXml(fileName);
pugi::xpath_node_set nodes = doc->select_nodes(xpath.toStdString().c_str());
for (pugi::xpath_node_set::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
result.append(it->node());
}
return result;
}
QList<pugi::xml_node> XmlTool::queryAndSort(const QString& fileName, const QString& xpath, const QList<QPair<QString, bool>>& sortRules) {
QList<pugi::xml_node> result = query(fileName, xpath);
return sortNodesByRules(result, sortRules);
}
pugi::xml_document* XmlTool::loadXml(const QString &fileName)
{
pugi::xml_document* doc = new pugi::xml_document();
if (!loadXmlStream(fileName, *doc)) {
delete doc;
xmlDocs_.remove(fileName);
} else {
xmlDocs_[fileName] = doc;
}
return doc;
}
void XmlTool::ensureDocumentLoaded(const QString& fileName) {
if (!xmlDocs_.contains(fileName)) {
loadXml(fileName);
}
}
bool XmlTool::processXmlChunk(const QString& chunk, pugi::xml_document& doc) {
pugi::xml_parse_result result = doc.load_buffer(chunk.toStdString().c_str(), chunk.size());
return result;
}
void XmlTool::ensureLock(const QString& fileName) {
if (!fileLocks_.contains(fileName)) {
fileLocks_[fileName] = new QMutex();
}
if (!fileConditions_.contains(fileName)) {
fileConditions_[fileName] = new QWaitCondition();
}
}
void XmlTool::waitForUnlock(const QString& fileName) {
fileLocks_[fileName]->lock();
fileConditions_[fileName]->wait(fileLocks_[fileName]);
fileLocks_[fileName]->unlock();
}
void XmlTool::releaseLock(const QString& fileName) {
fileLocks_[fileName]->lock();
fileConditions_[fileName]->wakeAll();
fileLocks_[fileName]->unlock();
}
QList<pugi::xml_node> XmlTool::sortNodesByRules(const QList<pugi::xml_node>& nodes, const QList<QPair<QString, bool>>& sortRules) {
QList<pugi::xml_node> sortedNodes = nodes;
std::sort(sortedNodes.begin(), sortedNodes.end(), [&](const pugi::xml_node& a, const pugi::xml_node& b) {
for (const auto& rule : sortRules) {
bool asc = rule.second;
QString attrName = rule.first.toStdString().c_str();
std::string attrA = a.attribute(attrName.toStdString().c_str()).as_string();
std::string attrB = b.attribute(attrName.toStdString().c_str()).as_string();
if (attrA != attrB) {
return (attrA < attrB) == asc;
}
}
return false;
});
return sortedNodes;
}
void XmlTool::sortNodesByTagName(pugi::xml_node& parent) {
std::vector<pugi::xml_node> nodes;
for (pugi::xml_node child : parent.children()) {
nodes.push_back(child);
}
std::sort(nodes.begin(), nodes.end(), [](const pugi::xml_node& a, const pugi::xml_node& b) {
return std::string(a.name()) < std::string(b.name());
});
for (pugi::xml_node node : parent.children()) {
parent.remove_child(node);
}
for (const auto& node : nodes) {
parent.append_copy(node);
}
}
void XmlTool::sortAttributesByAttributeName(pugi::xml_node& node) {
std::vector<pugi::xml_attribute> attrs;
for (pugi::xml_attribute attr : node.attributes()) {
attrs.push_back(attr);
}
std::sort(attrs.begin(), attrs.end(), [](const pugi::xml_attribute& a, const pugi::xml_attribute& b) {
return std::string(a.name()) < std::string(b.name());
});
for (const auto& attr : attrs) {
node.append_attribute(attr.name()) = attr.value();
}
}
QString XmlTool::xmlDocumentToString(const pugi::xml_document& doc) {
std::ostringstream oss;
doc.save(oss, "", pugi::format_default, pugi::encoding_utf8);
return QString::fromStdString(oss.str());
}
void XmlTool::sortNodeRecursively(pugi::xml_node& node) {
sortAttributesByAttributeName(node);
sortNodesByTagName(node);
for (pugi::xml_node child : node.children()) {
sortNodeRecursively(child);
}
}
bool XmlTool::hasAttribute(const pugi::xml_node& node, const QString &attributeName) {
return node.attribute(attributeName.toStdString().c_str()) != nullptr;
}
void XmlTool::add_indentation(pugi::xml_node node, int indent_level, std::ostringstream &oss) {
std::string indent(indent_level * 2, ' ');
if (node.type() == pugi::node_element) {
oss << "\n" << indent << "<" << node.name();
for (pugi::xml_attribute attr = node.first_attribute(); attr; attr = attr.next_attribute()) {
oss << " " << attr.name() << "=\"" << attr.value() << "\"";
}
oss << ">";
bool has_children = false;
for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) {
if (child.type() != pugi::node_pcdata) {
has_children = true;
break;
}
}
if (has_children) {
for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) {
add_indentation(child, indent_level + 1, oss);
}
oss << "\n" << indent << "</" << node.name() << ">";
} else {
std::string text = node.child_value();
if (text.find_first_not_of(" \t\n\r") != std::string::npos) {
oss << text;
}
oss << "</" << node.name() << ">";
}
} else if (node.type() == pugi::node_pcdata) {
std::string text = node.value();
text.erase(0, text.find_first_not_of(" \t\n\r"));
text.erase(text.find_last_not_of(" \t\n\r") + 1);
if (!text.empty()) {
oss << text;
}
}
}
void XmlTool::process_if_node(pugi::xml_node if_node, pugi::xml_node &parent_node) {
std::string condition = if_node.attribute("condition").as_string();
bool condition_met = (condition == "true");
if (condition_met) {
for (pugi::xml_node child = if_node.first_child(); child; child = child.next_sibling()) {
parent_node.insert_copy_before(child, if_node);
}
}
parent_node.remove_child(if_node);
}
void XmlTool::process_for_node(pugi::xml_node for_node, pugi::xml_node &parent_node) {
int count = for_node.attribute("count").as_int();
if (count <= 0) {
std::cerr << "Invalid 'count' attribute" << std::endl;
return;
}
std::vector<pugi::xml_node> children_to_copy;
for (pugi::xml_node child : for_node.children()) {
children_to_copy.push_back(child);
}
for (int i = 0; i < count; ++i) {
for (pugi::xml_node child : children_to_copy) {
pugi::xml_node new_node = parent_node.insert_copy_before(child, for_node);
process_all_if_nodes(new_node);
process_all_for_nodes(new_node);
}
}
parent_node.remove_child(for_node);
}
void XmlTool::process_all_if_nodes(pugi::xml_node node) {
pugi::xml_node next_node;
for (pugi::xml_node child = node.first_child(); child; child = next_node) {
next_node = child.next_sibling();
if (std::string(child.name()) == "if") {
process_if_node(child, node);
} else {
process_all_if_nodes(child);
}
}
}
void XmlTool::process_all_for_nodes(pugi::xml_node node) {
pugi::xml_node next_node;
for (pugi::xml_node child = node.first_child(); child; child = next_node) {
next_node = child.next_sibling();
if (std::string(child.name()) == "for") {
process_for_node(child, node);
} else {
process_all_for_nodes(child);
}
}
}