diff options
Diffstat (limited to 'sources')
-rw-r--r-- | sources/shiboken2/ApiExtractor/CMakeLists.txt | 26 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/docparser.cpp | 183 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/docparser.h | 17 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/doxygenparser.cpp | 19 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/qtdocparser.cpp | 21 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/qtdocparser.h | 2 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp | 11 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils.cpp | 68 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils.h | 53 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp | 231 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils_libxslt.h | 40 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils_qt.cpp | 102 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/xmlutils_qt.h | 40 | ||||
-rw-r--r-- | sources/shiboken2/CMakeLists.txt | 7 | ||||
-rw-r--r-- | sources/shiboken2/generator/CMakeLists.txt | 1 |
15 files changed, 631 insertions, 190 deletions
diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index 147fda377..760cc6985 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -26,6 +26,7 @@ clangparser/clangutils.cpp # Old parser parser/codemodel.cpp parser/enumvalue.cpp +xmlutils.cpp ) add_library(apiextractor STATIC ${apiextractor_SRC}) @@ -37,19 +38,26 @@ target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} target_link_libraries(apiextractor PUBLIC Qt5::Core) target_link_libraries(apiextractor PRIVATE ${CLANG_EXTRA_LIBRARIES}) +if (HAS_LIBXSLT) + target_compile_definitions(apiextractor PUBLIC HAVE_LIBXSLT) + target_sources(apiextractor PRIVATE xmlutils_libxslt.cpp) + target_include_directories(apiextractor + PRIVATE ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) + target_link_libraries(apiextractor + PRIVATE ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) +endif() + +if (Qt5XmlPatterns_FOUND) + target_compile_definitions(apiextractor PUBLIC HAVE_QTXMLPATTERNS) + target_sources(apiextractor PRIVATE xmlutils_qt.cpp) + target_link_libraries(apiextractor PUBLIC Qt5::Xml Qt5::XmlPatterns) +endif() + if (NOT DISABLE_DOCSTRINGS) target_sources(apiextractor PRIVATE docparser.cpp doxygenparser.cpp qtdocparser.cpp) - target_link_libraries(apiextractor PUBLIC Qt5::Xml Qt5::XmlPatterns) - - if (LIBXSLT_FOUND AND LIBXML2_FOUND) - target_compile_definitions(apiextractor PUBLIC HAVE_LIBXSLT) - target_include_directories(apiextractor - PRIVATE ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) - target_link_libraries(apiextractor - PRIVATE ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) - else() + if (NOT HAS_LIBXSLT) message(WARNING "libxslt and/or libxml not found, falling back to QtXmlPatterns (QTBUG-66925)") endif() diff --git a/sources/shiboken2/ApiExtractor/docparser.cpp b/sources/shiboken2/ApiExtractor/docparser.cpp index 99921e7d3..532956d1a 100644 --- a/sources/shiboken2/ApiExtractor/docparser.cpp +++ b/sources/shiboken2/ApiExtractor/docparser.cpp @@ -30,10 +30,10 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QTextStream> -#include <QtXmlPatterns/QXmlQuery> #include <QBuffer> #include <cstdlib> @@ -53,27 +53,19 @@ DocParser::DocParser() DocParser::~DocParser() = default; -QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query, +QString DocParser::getDocumentation(const XQueryPtr &xquery, const QString& query, const DocModificationList& mods) const { QString doc = execXQuery(xquery, query); return applyDocModifications(mods, doc.trimmed()); } -QString DocParser::execXQuery(QXmlQuery& xquery, const QString& query) const +QString DocParser::execXQuery(const XQueryPtr &xquery, const QString& query) const { - QString escapedQuery(query); - // XQuery can't have invalid XML characters - escapedQuery.replace(QLatin1Char('&'), QLatin1String("&")); - escapedQuery.replace(QLatin1Char('<'), QLatin1String("<")); - xquery.setQuery(escapedQuery); - if (!xquery.isValid()) { - qWarning() << "Bad XQuery: " << escapedQuery; - return QString(); - } - - QString result; - xquery.evaluateTo(&result); + QString errorMessage; + const QString result = xquery->evaluate(query, &errorMessage); + if (!errorMessage.isEmpty()) + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); return result; } @@ -108,41 +100,6 @@ AbstractMetaFunctionList DocParser::documentableFunctions(const AbstractMetaClas return result; } - -#ifdef HAVE_LIBXSLT -namespace -{ - -class XslResources -{ - Q_DISABLE_COPY(XslResources) - -public: - xmlDocPtr xmlDoc = nullptr; - xsltStylesheetPtr xslt = nullptr; - xmlDocPtr xslResult = nullptr; - - XslResources() = default; - - ~XslResources() - { - if (xslt) - xsltFreeStylesheet(xslt); - - if (xslResult) - xmlFreeDoc(xslResult); - - if (xmlDoc) - xmlFreeDoc(xmlDoc); - - xsltCleanupGlobals(); - xmlCleanupParser(); - } -}; - -} // namespace -#endif // HAVE_LIBXSLT - static inline bool isXpathDocModification(const DocModification &mod) { return mod.mode() == TypeSystem::DocModificationXPathReplace; @@ -150,102 +107,22 @@ static inline bool isXpathDocModification(const DocModification &mod) QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const { + const char xslPrefix[] = +R"(<xsl:template match="/"> + <xsl:apply-templates /> +</xsl:template> +<xsl:template match="*"> +<xsl:copy> + <xsl:copy-of select="@*"/> + <xsl:apply-templates/> +</xsl:copy> +</xsl:template> +)"; + if (mods.isEmpty() || xml.isEmpty() || !std::any_of(mods.cbegin(), mods.cend(), isXpathDocModification)) { return xml; } -#ifdef HAVE_LIBXSLT - const QString result = applyDocModificationsLibXsl(mods, xml); -#else - const QString result = applyDocModificationsQt(mods, xml); -#endif - if (result == xml) { - const QString message = QLatin1String("Query did not result in any modifications to \"") - + xml + QLatin1Char('"'); - qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, message))); - } - return result; -} - -QString DocParser::applyDocModificationsLibXsl(const DocModificationList& mods, const QString& xml) const -{ -#ifdef HAVE_LIBXSLT - QString xsl = QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" - "<xsl:transform version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" - "<xsl:template match=\"/\">\n" - " <xsl:apply-templates />\n" - "</xsl:template>\n" - "<xsl:template match=\"*\">\n" - "<xsl:copy>\n" - " <xsl:copy-of select=\"@*\"/>\n" - " <xsl:apply-templates/>\n" - "</xsl:copy>\n" - "</xsl:template>\n" - ); - for (const DocModification &mod : mods) { - if (isXpathDocModification(mod)) { - QString xpath = mod.xpath(); - xpath.replace(QLatin1Char('"'), QLatin1String(""")); - xsl += QLatin1String("<xsl:template match=\"") - + xpath + QLatin1String("\">") - + mod.code() + QLatin1String("</xsl:template>\n"); - } - } - xsl += QLatin1String("</xsl:transform>"); - - XslResources res; - // Read XML data - QByteArray xmlData = xml.toUtf8(); - res.xmlDoc = xmlParseMemory(xmlData.constData(), xmlData.size()); - if (!res.xmlDoc) - return xml; - - // Read XSL data as a XML file - QByteArray xslData = xsl.toUtf8(); - // xsltFreeStylesheet will delete this pointer - xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size()); - if (!xslDoc) - return xml; - - // Parse XSL data - res.xslt = xsltParseStylesheetDoc(xslDoc); - if (!res.xslt) - return xml; - - // Apply XSL - res.xslResult = xsltApplyStylesheet(res.xslt, res.xmlDoc, 0); - xmlChar* buffer = 0; - int bufferSize; - QString result; - if (!xsltSaveResultToString(&buffer, &bufferSize, res.xslResult, res.xslt)) { - result = QString::fromUtf8(reinterpret_cast<char*>(buffer), bufferSize); - std::free(buffer); - } else { - result = xml; - } - return result.trimmed(); -#else // HAVE_LIBXSLT - Q_UNUSED(mods) - return xml; -#endif // !HAVE_LIBXSLT -} - -QString DocParser::applyDocModificationsQt(const DocModificationList& mods, const QString& xml) const -{ - const char xslPrefix[] = -R"(<?xml version="1.0" encoding="UTF-8"?> -<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> - <xsl:template match="/"> - <xsl:apply-templates/>\n" - </xsl:template> - <xsl:template match="*"> - <xsl:copy> - <xsl:copy-of select="@*"/> - <xsl:apply-templates/> - </xsl:copy> - </xsl:template> -)"; QString xsl = QLatin1String(xslPrefix); for (const DocModification &mod : mods) { @@ -257,21 +134,17 @@ R"(<?xml version="1.0" encoding="UTF-8"?> + mod.code() + QLatin1String("</xsl:template>\n"); } } - xsl += QLatin1String("</xsl:stylesheet>"); - QXmlQuery query(QXmlQuery::XSLT20); - query.setFocus(xml); - query.setQuery(xsl); - if (!query.isValid()) { + QString errorMessage; + const QString result = xsl_transform(xml, xsl, &errorMessage); + if (!errorMessage.isEmpty()) qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, QLatin1String("Invalid query.")))); - return xml; - } - QString result; - if (!query.evaluateTo(&result)) { + qPrintable(msgXpathDocModificationError(mods, errorMessage))); + if (result == xml) { + const QString message = QLatin1String("Query did not result in any modifications to \"") + + xml + QLatin1Char('"'); qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, QLatin1String("evaluate() failed.")))); - return xml; + qPrintable(msgXpathDocModificationError(mods, message))); } - return result.trimmed(); + return result; } diff --git a/sources/shiboken2/ApiExtractor/docparser.h b/sources/shiboken2/ApiExtractor/docparser.h index 7cbbef28e..53507b0f5 100644 --- a/sources/shiboken2/ApiExtractor/docparser.h +++ b/sources/shiboken2/ApiExtractor/docparser.h @@ -32,20 +32,19 @@ #include "abstractmetalang_typedefs.h" #include <QtCore/QString> - -QT_BEGIN_NAMESPACE -class QDomDocument; -class QDomNode; -class QXmlQuery; -QT_END_NAMESPACE +#include <QtCore/QSharedPointer> class AbstractMetaClass; class DocModification; class Documentation; +class XQuery; + class DocParser { public: + using XQueryPtr = QSharedPointer<XQuery>; + DocParser(); virtual ~DocParser(); virtual void fillDocumentation(AbstractMetaClass* metaClass) = 0; @@ -114,7 +113,7 @@ public: static bool skipForQuery(const AbstractMetaFunction *func); protected: - QString getDocumentation(QXmlQuery& xquery, const QString& query, + QString getDocumentation(const XQueryPtr &xquery, const QString& query, const DocModificationList& mods) const; @@ -125,10 +124,8 @@ private: QString m_docDataDir; QString m_libSourceDir; - QString execXQuery(QXmlQuery& xquery, const QString& query) const; + QString execXQuery(const XQueryPtr &xquery, const QString& query) const; QString applyDocModifications(const DocModificationList& mods, const QString& xml) const; - QString applyDocModificationsLibXsl(const DocModificationList& mods, const QString& xml) const; - QString applyDocModificationsQt(const DocModificationList& mods, const QString& xml) const; }; #endif // DOCPARSER_H diff --git a/sources/shiboken2/ApiExtractor/doxygenparser.cpp b/sources/shiboken2/ApiExtractor/doxygenparser.cpp index e238aa1f7..94c9ec7e0 100644 --- a/sources/shiboken2/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken2/ApiExtractor/doxygenparser.cpp @@ -31,8 +31,8 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" -#include <QtXmlPatterns/QXmlQuery> #include <QtCore/QFile> #include <QtCore/QDir> @@ -86,8 +86,13 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) << "/{struct|class|namespace}"<< doxyFileSuffix; return; } - QXmlQuery xquery; - xquery.setFocus(QUrl(doxyFilePath)); + + QString errorMessage; + XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return; + } // Get class documentation const QString classQuery = QLatin1String("/doxygen/compounddef/detaileddescription"); @@ -190,8 +195,12 @@ Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){ return Documentation(); } - QXmlQuery xquery; - xquery.setFocus(QUrl(sourceFile)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return {}; + } // Module documentation QString query = QLatin1String("/doxygen/compounddef/detaileddescription"); diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index 809760450..c5ee1743d 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -31,8 +31,8 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" -#include <QtXmlPatterns/QXmlQuery> #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QTextStream> @@ -161,7 +161,7 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const QString &classQuery, const AbstractMetaFunction *func, const DocModificationList &signedModifs, - QXmlQuery &xquery, + const XQueryPtr &xquery, QString *errorMessage) { DocModificationList funcModifs; @@ -231,9 +231,13 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) return; } - QXmlQuery xquery; const QString sourceFileName = sourceFile.absoluteFilePath(); - xquery.setFocus(QUrl::fromLocalFile(sourceFileName)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFileName, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return; + } QString className = metaClass->name(); @@ -258,7 +262,6 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) metaClass->setDocumentation(doc); //Functions Documentation - QString errorMessage; const AbstractMetaFunctionList &funcs = DocParser::documentableFunctions(metaClass); for (AbstractMetaFunction *func : funcs) { const QString documentation = @@ -324,8 +327,12 @@ Documentation QtDocParser::retrieveModuleDocumentation(const QString& name) return Documentation(); } - QXmlQuery xquery; - xquery.setFocus(QUrl(sourceFile)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return {}; + } // Module documentation QString query = QLatin1String("/WebXML/document/module[@name=\"") diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.h b/sources/shiboken2/ApiExtractor/qtdocparser.h index b5f0e51d8..c4333e820 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.h +++ b/sources/shiboken2/ApiExtractor/qtdocparser.h @@ -45,7 +45,7 @@ private: const QString &classQuery, const AbstractMetaFunction *func, const DocModificationList &signedModifs, - QXmlQuery &xquery, + const XQueryPtr &xquery, QString *errorMessage); }; diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index f615befb4..6acac41d5 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -29,6 +29,7 @@ #include "testmodifydocumentation.h" #include <QCoreApplication> +#include <QtCore/QTemporaryDir> #include <QtTest/QTest> #include "testutil.h" #include <abstractmetalang.h> @@ -59,8 +60,16 @@ R"(<typesystem package="Foo"> QCOMPARE(docMods[0].signature(), QString()); QCOMPARE(docMods[1].code().trimmed(), QLatin1String("<para>Some changed contents here</para>")); QCOMPARE(docMods[1].signature(), QString()); + + // Create a temporary directory for the documentation file since libxml2 + // cannot handle Qt resources. + QTemporaryDir tempDir(QDir::tempPath() + QLatin1String("/shiboken_testmodifydocXXXXXX")); + QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString())); + const QString docFileName = QLatin1String("a.xml"); + QVERIFY(QFile::copy(QLatin1String(":/") + docFileName, tempDir.filePath(docFileName))); + QtDocParser docParser; - docParser.setDocumentationDataDirectory(QLatin1String(":")); + docParser.setDocumentationDataDirectory(tempDir.path()); docParser.fillDocumentation(classA); const QString actualDocSimplified = classA->documentation().value().simplified(); diff --git a/sources/shiboken2/ApiExtractor/xmlutils.cpp b/sources/shiboken2/ApiExtractor/xmlutils.cpp new file mode 100644 index 000000000..a179412a7 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xmlutils.h" + +#include "xmlutils_qt.h" +#include "xmlutils_libxslt.h" + +XQuery::XQuery() = default; + +XQuery::~XQuery() = default; + +QString XQuery::evaluate(QString xPathExpression, QString *errorMessage) +{ + // XQuery can't have invalid XML characters + xPathExpression.replace(QLatin1Char('&'), QLatin1String("&")); + xPathExpression.replace(QLatin1Char('<'), QLatin1String("<")); + return doEvaluate(xPathExpression, errorMessage); +} + +QSharedPointer<XQuery> XQuery::create(const QString &focus, QString *errorMessage) +{ +#if defined(HAVE_LIBXSLT) + return libXml_createXQuery(focus, errorMessage); +#elif defined(HAVE_QTXMLPATTERNS) + return qt_createXQuery(focus, errorMessage); +#else + *errorMessage = QLatin1String(__FUNCTION__) + QLatin1String(" is not implemented."); + return QSharedPointer<XQuery>(); +#endif +} + +QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage) +{ +#if defined(HAVE_LIBXSLT) + return libXslt_transform(xml, xsl, errorMessage); +#elif defined(HAVE_QTXMLPATTERNS) + return qt_xsl_transform(xml, xsl, errorMessage); +#else + *errorMessage = QLatin1String(__FUNCTION__) + QLatin1String(" is not implemented."); + return xml; +#endif +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils.h b/sources/shiboken2/ApiExtractor/xmlutils.h new file mode 100644 index 000000000..879b7757a --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef XMLUTILS_H +#define XMLUTILS_H + +#include <QtCore/QSharedPointer> +#include <QtCore/QString> + +class XQuery +{ +public: + Q_DISABLE_COPY(XQuery); + + virtual ~XQuery(); + + QString evaluate(QString xPathExpression, QString *errorMessage); + + static QSharedPointer<XQuery> create(const QString &focus, QString *errorMessage); + +protected: + XQuery(); + + virtual QString doEvaluate(const QString &xPathExpression, QString *errorMessage) = 0; +}; + +QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage); + +#endif // XMLUTILS_H diff --git a/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp new file mode 100644 index 000000000..e1e185130 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xmlutils_libxslt.h" +#include "xmlutils.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> + +static void cleanup() +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} + +static void ensureInitialized() +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + xmlInitParser(); + xsltInit(); + qAddPostRoutine(cleanup); + } +} + +namespace { + +// 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); } +}; + +} // namespace + +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 = QLatin1String("xmlSaveToIO() failed."); + return result; + } + const long saveResult = xmlSaveTree(saveContext, node); + xmlSaveClose(saveContext); + if (saveResult < 0) + *errorMessage = QLatin1String("xmlSaveTree() failed."); + 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 = QLatin1String("xmlXPathEvalExpression() failed for \"") + xPathExpression + + QLatin1Char('"'); + 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; +} + +QSharedPointer<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage) +{ + XmlDocUniquePtr doc(xmlParseFile(QFile::encodeName(focus).constData())); + if (!doc) { + *errorMessage = QLatin1String("libxml2: Cannot set focus to ") + QDir::toNativeSeparators(focus); + return {}; + } + XmlXPathContextUniquePtr xpathContext(xmlXPathNewContext(doc.get())); + if (!xpathContext) { + *errorMessage = QLatin1String("libxml2: xmlXPathNewContext() failed"); + return {}; + } + return QSharedPointer<XQuery>(new LibXmlXQuery(doc, xpathContext)); +} + +// XSLT transformation + +static const char xsltPrefix[] = R"(<?xml version="1.0" encoding="UTF-8" ?> + <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +)"; + +QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage) +{ + ensureInitialized(); + // Read XML data + if (!xsl.startsWith(QLatin1String("<?xml"))) { + xsl.prepend(QLatin1String(xsltPrefix)); + xsl.append(QLatin1String("</xsl:transform>")); + } + const QByteArray xmlData = xml.toUtf8(); + XmlDocUniquePtr xmlDoc(xmlParseMemory(xmlData.constData(), xmlData.size())); + if (!xmlDoc) { + *errorMessage = QLatin1String("xmlParseMemory() failed for XML."); + 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 = QLatin1String("xmlParseMemory() failed for XSL \"") + + xsl + QLatin1String("\"."); + return xml; + }; + + // Parse XSL data + XmlStyleSheetUniquePtr xslt(xsltParseStylesheetDoc(xslDoc)); + if (!xslt) { + *errorMessage = QLatin1String("xsltParseStylesheetDoc() failed."); + 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 = QLatin1String("xsltSaveResultToString() failed."); + result = xml; + } + return result.trimmed(); +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h new file mode 100644 index 000000000..c32b3901d --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef XMLUTILS_LIBXSLT_H +#define XMLUTILS_LIBXSLT_H + +#include <QtCore/QString> +#include <QtCore/QSharedPointer> + +class XQuery; + +QSharedPointer<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage); + +QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage); + +#endif // XMLUTILS_LIBXSLT_H diff --git a/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp b/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp new file mode 100644 index 000000000..f703bc5f9 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xmlutils.h" +#include "xmlutils_qt.h" + +#include <QtXmlPatterns/QXmlQuery> + +#include <QtCore/QDir> +#include <QtCore/QUrl> + +class QtXQuery : public XQuery +{ +public: + QtXQuery() = default; + + bool setFocus(const QString &fileName) + { return m_xquery.setFocus(QUrl::fromLocalFile(fileName)); } + +protected: + QString doEvaluate(const QString &xPathExpression, QString *errorMessage) override; + +private: + QXmlQuery m_xquery; +}; + +QString QtXQuery::doEvaluate(const QString &xPathExpression, QString *errorMessage) +{ + m_xquery.setQuery(xPathExpression); + if (!m_xquery.isValid()) { + *errorMessage = QLatin1String("QXmlQuery: Bad query: \"") + xPathExpression + + QLatin1Char('"'); + return QString(); + } + + QString result; + m_xquery.evaluateTo(&result); + return result; +} + +QSharedPointer<XQuery> qt_createXQuery(const QString &focus, QString *errorMessage) +{ + QSharedPointer<QtXQuery> result(new QtXQuery); + if (!result->setFocus(focus)) { + *errorMessage = QLatin1String("QXmlQuery: Cannot set focus to ") + QDir::toNativeSeparators(focus); + result.reset(); + } + return std::move(result); +} + +// XSLT transformation + +static const char xsltPrefix[] = R"(<?xml version="1.0" encoding="UTF-8"?> + <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +)"; + +QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage) +{ + QXmlQuery query(QXmlQuery::XSLT20); + if (!xsl.startsWith(QLatin1String("<?xml"))) { + xsl.prepend(QLatin1String(xsltPrefix)); + xsl.append(QLatin1String("</xsl:stylesheet>")); + } + query.setFocus(xml); + query.setQuery(xsl); + if (!query.isValid()) { + *errorMessage = QLatin1String("QXmlQuery: Invalid query \"") + xsl + + QLatin1String("\"."); + return xml; + } + QString result; + if (!query.evaluateTo(&result)) { + *errorMessage = QLatin1String("QXmlQuery: evaluate() failed."); + return xml; + } + return result.trimmed(); +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils_qt.h b/sources/shiboken2/ApiExtractor/xmlutils_qt.h new file mode 100644 index 000000000..a9c9adfa2 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_qt.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef XMLUTILS_QT_H +#define XMLUTILS_QT_H + +#include <QtCore/QString> +#include <QtCore/QSharedPointer> + +class XQuery; + +QSharedPointer<XQuery> qt_createXQuery(const QString &focus, QString *errorMessage); + +QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage); + +#endif // XMLUTILS_QT_H diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 12429c657..950aa6215 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -24,7 +24,12 @@ if(BUILD_TESTS) find_package(Qt5Test 5.12 REQUIRED) endif() -if(NOT Qt5XmlPatterns_FOUND OR NOT Qt5Xml_FOUND) +set(HAS_LIBXSLT 0) +if (LIBXSLT_FOUND AND LIBXML2_FOUND) + set(HAS_LIBXSLT 1) +endif() + +if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) set(DISABLE_DOCSTRINGS TRUE) message(WARNING "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") diff --git a/sources/shiboken2/generator/CMakeLists.txt b/sources/shiboken2/generator/CMakeLists.txt index 745c366f5..15b965d9d 100644 --- a/sources/shiboken2/generator/CMakeLists.txt +++ b/sources/shiboken2/generator/CMakeLists.txt @@ -24,7 +24,6 @@ target_link_libraries(shiboken2 apiextractor Qt5::Core) if (NOT DISABLE_DOCSTRINGS) target_sources(shiboken2 PRIVATE qtdoc/qtdocgenerator.cpp) target_compile_definitions(shiboken2 PUBLIC DOCSTRINGS_ENABLED) - target_link_libraries(shiboken2 Qt5::XmlPatterns) endif() configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) |