aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp
new file mode 100644
index 000000000..5a9a26913
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp
@@ -0,0 +1,208 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "xmlutils_libxslt.h"
+#include "xmlutils.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+
+#include <libxml/xmlsave.h>
+#include <libxml/xpath.h>
+
+#include <cstdlib>
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+static void cleanup()
+{
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+}
+
+static void ensureInitialized()
+{
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ xmlInitParser();
+ xsltInit();
+ qAddPostRoutine(cleanup);
+ }
+}
+
+// RAI Helpers for cleaning up libxml2/libxslt data
+
+struct XmlDocDeleter // for std::unique_ptr<xmlDoc>
+{
+ void operator()(xmlDocPtr xmlDoc) { xmlFreeDoc(xmlDoc); }
+};
+
+struct XmlXPathObjectDeleter
+{
+ void operator()(xmlXPathObjectPtr xPathObject) { xmlXPathFreeObject(xPathObject); }
+};
+
+struct XmlStyleSheetDeleter // for std::unique_ptr<xsltStylesheet>
+{
+ void operator()(xsltStylesheetPtr xslt) { xsltFreeStylesheet(xslt); }
+};
+
+struct XmlXPathContextDeleter
+{
+ void operator()(xmlXPathContextPtr xPathContext) { xmlXPathFreeContext(xPathContext); }
+};
+
+using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
+using XmlPathObjectUniquePtr = std::unique_ptr<xmlXPathObject, XmlXPathObjectDeleter>;
+using XmlStyleSheetUniquePtr = std::unique_ptr<xsltStylesheet, XmlStyleSheetDeleter>;
+using XmlXPathContextUniquePtr = std::unique_ptr<xmlXPathContext, XmlXPathContextDeleter>;
+
+// Helpers for formatting nodes obtained from XPATH queries
+
+static int qbXmlOutputWriteCallback(void *context, const char *buffer, int len)
+{
+ static_cast<QByteArray *>(context)->append(buffer, len);
+ return len;
+}
+
+static int qbXmlOutputCloseCallback(void * /* context */) { return 0; }
+
+static QByteArray formatNode(xmlNodePtr node, QString *errorMessage)
+{
+ QByteArray result;
+ xmlSaveCtxtPtr saveContext =
+ xmlSaveToIO(qbXmlOutputWriteCallback, qbXmlOutputCloseCallback,
+ &result, "UTF-8", 0);
+ if (!saveContext) {
+ *errorMessage = u"xmlSaveToIO() failed."_s;
+ return result;
+ }
+ const long saveResult = xmlSaveTree(saveContext, node);
+ xmlSaveClose(saveContext);
+ if (saveResult < 0)
+ *errorMessage = u"xmlSaveTree() failed."_s;
+ return result;
+}
+
+// XPath expressions
+class LibXmlXQuery : public XQuery
+{
+public:
+ explicit LibXmlXQuery(XmlDocUniquePtr &doc, XmlXPathContextUniquePtr &xpathContext) :
+ m_doc(std::move(doc)), m_xpathContext(std::move(xpathContext))
+ {
+ ensureInitialized();
+ }
+
+protected:
+ QString doEvaluate(const QString &xPathExpression, QString *errorMessage) override;
+
+private:
+ XmlDocUniquePtr m_doc;
+ XmlXPathContextUniquePtr m_xpathContext;
+};
+
+QString LibXmlXQuery::doEvaluate(const QString &xPathExpression, QString *errorMessage)
+{
+ const QByteArray xPathExpressionB = xPathExpression.toUtf8();
+ auto xPathExpressionX = reinterpret_cast<const xmlChar *>(xPathExpressionB.constData());
+
+ XmlPathObjectUniquePtr xPathObject(xmlXPathEvalExpression(xPathExpressionX, m_xpathContext.get()));
+ if (!xPathObject) {
+ *errorMessage = u"xmlXPathEvalExpression() failed for \""_s + xPathExpression
+ + u'"';
+ return QString();
+ }
+ QString result;
+ if (xmlNodeSetPtr nodeSet = xPathObject->nodesetval) {
+ for (int n = 0, count = nodeSet->nodeNr; n < count; ++n) {
+ auto node = nodeSet->nodeTab[n];
+ if (node->type == XML_ELEMENT_NODE) {
+ result += QString::fromLocal8Bit(formatNode(node, errorMessage));
+ if (!errorMessage->isEmpty())
+ return QString();
+ }
+ }
+ }
+ return result;
+}
+
+std::shared_ptr<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage)
+{
+ XmlDocUniquePtr doc(xmlReadFile(QFile::encodeName(focus).constData(),
+ "utf-8", XML_PARSE_NOENT));
+ if (!doc) {
+ *errorMessage = u"libxml2: Cannot set focus to "_s + QDir::toNativeSeparators(focus);
+ return {};
+ }
+ XmlXPathContextUniquePtr xpathContext(xmlXPathNewContext(doc.get()));
+ if (!xpathContext) {
+ *errorMessage = u"libxml2: xmlXPathNewContext() failed"_s;
+ return {};
+ }
+ return std::shared_ptr<XQuery>(new LibXmlXQuery(doc, xpathContext));
+}
+
+// XSLT transformation
+
+static constexpr auto xsltPrefix = R"(<?xml version="1.0" encoding="UTF-8" ?>
+ <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+)"_L1;
+
+QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage)
+{
+ ensureInitialized();
+ // Read XML data
+ if (!xsl.startsWith(u"<?xml")) {
+ xsl.prepend(xsltPrefix);
+ xsl.append(u"</xsl:transform>"_s);
+ }
+ const QByteArray xmlData = xml.toUtf8();
+
+ XmlDocUniquePtr xmlDoc(xmlReadMemory(xmlData.constData(), int(xmlData.size()),
+ "", "utf-8", XML_PARSE_NOENT));
+ if (!xmlDoc) {
+ *errorMessage = u"xmlParseMemory() failed for XML."_s;
+ return xml;
+ }
+
+ // Read XSL data as a XML file
+ const QByteArray xslData = xsl.toUtf8();
+ // xsltFreeStylesheet will delete this pointer
+ xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size());
+ if (!xslDoc) {
+ *errorMessage = u"xmlParseMemory() failed for XSL \""_s + xsl + u"\"."_s;
+ return xml;
+ };
+
+ // Parse XSL data
+ XmlStyleSheetUniquePtr xslt(xsltParseStylesheetDoc(xslDoc));
+ if (!xslt) {
+ *errorMessage = u"xsltParseStylesheetDoc() failed."_s;
+ return xml;
+ }
+
+ // Apply XSL
+ XmlDocUniquePtr xslResult(xsltApplyStylesheet(xslt.get(), xmlDoc.get(), nullptr));
+ xmlChar *buffer = nullptr;
+ int bufferSize;
+ QString result;
+ if (xsltSaveResultToString(&buffer, &bufferSize, xslResult.get(), xslt.get()) == 0) {
+ result = QString::fromUtf8(reinterpret_cast<char*>(buffer), bufferSize);
+ std::free(buffer);
+ } else {
+ *errorMessage = u"xsltSaveResultToString() failed."_s;
+ result = xml;
+ }
+ return result.trimmed();
+}