diff options
author | Paul Wicking <paul.wicking@qt.io> | 2020-09-10 14:28:40 +0200 |
---|---|---|
committer | Paul Wicking <paul.wicking@qt.io> | 2020-09-11 09:38:33 +0200 |
commit | 93e805a02417290f225810ce7c9263b2fe56b4e5 (patch) | |
tree | de42b565e4fd3495a9a9df77f3d1234a89054f02 | |
parent | f72b7a68221898333c3ea032d493a9efd5a68af6 (diff) |
QDoc: Extract ManifestWriter from HTML generator
Fixes: QTBUG-86390
Change-Id: I9cdcb591bbd1cf6beccf27e53ab9af06b87ed4be
Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
-rw-r--r-- | src/qdoc/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qdoc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qdoc/htmlgenerator.cpp | 290 | ||||
-rw-r--r-- | src/qdoc/htmlgenerator.h | 7 | ||||
-rw-r--r-- | src/qdoc/manifestwriter.cpp | 328 | ||||
-rw-r--r-- | src/qdoc/manifestwriter.h | 67 | ||||
-rw-r--r-- | src/qdoc/qdoc.pro | 2 |
7 files changed, 412 insertions, 284 deletions
diff --git a/src/qdoc/.prev_CMakeLists.txt b/src/qdoc/.prev_CMakeLists.txt index eebdf59a1..e65c0e9a4 100644 --- a/src/qdoc/.prev_CMakeLists.txt +++ b/src/qdoc/.prev_CMakeLists.txt @@ -42,6 +42,7 @@ qt_add_tool(${target_name} loggingcategory.h macro.h main.cpp + manifestwriter.cpp manifestwriter.h namespacenode.cpp namespacenode.h node.cpp node.h openedlist.cpp openedlist.h diff --git a/src/qdoc/CMakeLists.txt b/src/qdoc/CMakeLists.txt index bc7e29d54..f5904e29b 100644 --- a/src/qdoc/CMakeLists.txt +++ b/src/qdoc/CMakeLists.txt @@ -50,6 +50,7 @@ qt_add_tool(${target_name} loggingcategory.h macro.h main.cpp + manifestwriter.cpp manifestwriter.h namespacenode.cpp namespacenode.h node.cpp node.h openedlist.cpp openedlist.h diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp index 6dc7dc93d..3cc08c84b 100644 --- a/src/qdoc/htmlgenerator.cpp +++ b/src/qdoc/htmlgenerator.cpp @@ -39,6 +39,7 @@ #include "examplenode.h" #include "functionnode.h" #include "helpprojectwriter.h" +#include "manifestwriter.h" #include "node.h" #include "propertynode.h" #include "qdocdatabase.h" @@ -79,7 +80,7 @@ static void addLink(const QString &linkTarget, QStringView nestedStuff, QString /*! Destroys the HTML output generator. Deletes the singleton - instance of HelpProjectWriter. + instance of HelpProjectWriter and the ManifestWriter instance. */ HtmlGenerator::~HtmlGenerator() { @@ -87,6 +88,11 @@ HtmlGenerator::~HtmlGenerator() delete m_helpProjectWriter; m_helpProjectWriter = nullptr; } + + if (m_manifestWriter) { + delete m_manifestWriter; + m_manifestWriter = nullptr; + } } /*! @@ -172,21 +178,14 @@ void HtmlGenerator::initializeGenerator() else m_helpProjectWriter = new HelpProjectWriter(m_project.toLower() + ".qhp", this); + if (!m_manifestWriter) + m_manifestWriter = new ManifestWriter(); + // Documentation template handling m_headerScripts = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS); m_headerStyles = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES); - QString prefix = CONFIG_QHP + Config::dot + m_project + Config::dot; - m_manifestDir = - QLatin1String("qthelp://") + config->getString(prefix + QLatin1String("namespace")); - m_manifestDir += QLatin1Char('/') + config->getString(prefix + QLatin1String("virtualFolder")) - + QLatin1Char('/'); - readManifestMetaContent(); - m_examplesPath = config->getString(CONFIG_EXAMPLESINSTALLPATH); - if (!m_examplesPath.isEmpty()) - m_examplesPath += QLatin1Char('/'); - // Retrieve the config for the navigation bar m_homepage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_HOMEPAGE); @@ -259,7 +258,7 @@ void HtmlGenerator::generateDocs() if (!config->preparing()) { m_helpProjectWriter->generate(); - generateManifestFiles(); + m_manifestWriter->generateManifestFiles(); /* Generate the XML tag file, if it was requested. */ @@ -3660,271 +3659,4 @@ void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType } } -/*! - This function outputs one or more manifest files in XML. - They are used by Creator. - */ -void HtmlGenerator::generateManifestFiles() -{ - generateManifestFile("examples", "example"); - generateManifestFile("demos", "demo"); - m_qdb->exampleNodeMap().clear(); - m_manifestMetaContent.clear(); -} - -/*! - Retrieve the install path for the \a example as specified with - the \meta command, or fall back to the one defined in .qdocconf. - */ -QString HtmlGenerator::retrieveInstallPath(const ExampleNode *example) -{ - QString installPath; - if (example->doc().metaTagMap()) - installPath = example->doc().metaTagMap()->value(QLatin1String("installpath")); - if (installPath.isEmpty()) - installPath = m_examplesPath; - if (!installPath.isEmpty() && !installPath.endsWith(QLatin1Char('/'))) - installPath += QLatin1Char('/'); - - return installPath; -} - -/*! - This function is called by generateManifestFiles(), once - for each manifest file to be generated. \a manifest is the - type of manifest file. - */ -void HtmlGenerator::generateManifestFile(const QString &manifest, const QString &element) -{ - ExampleNodeMap &exampleNodeMap = m_qdb->exampleNodeMap(); - if (exampleNodeMap.isEmpty()) - return; - QString fileName = manifest + "-manifest.xml"; - QFile file(outputDir() + QLatin1Char('/') + fileName); - bool demos = false; - if (manifest == QLatin1String("demos")) - demos = true; - - bool proceed = false; - for (auto map = exampleNodeMap.begin(); map != exampleNodeMap.end(); ++map) { - const ExampleNode *en = map.value(); - if (demos == en->name().startsWith("demos")) { - proceed = true; - break; - } - } - if (!proceed || !file.open(QFile::WriteOnly | QFile::Text)) - return; - - QXmlStreamWriter writer(&file); - writer.setAutoFormatting(true); - writer.writeStartDocument(); - writer.writeStartElement("instructionals"); - writer.writeAttribute("module", m_project); - writer.writeStartElement(manifest); - - QStringList usedAttributes; - for (auto map = exampleNodeMap.begin(); map != exampleNodeMap.end(); ++map) { - const ExampleNode *en = map.value(); - if (demos) { - if (!en->name().startsWith("demos")) - continue; - } else if (en->name().startsWith("demos")) { - continue; - } - - const QString installPath = retrieveInstallPath(en); - // attributes that are always written for the element - usedAttributes.clear(); - usedAttributes << "name" - << "docUrl" - << "projectPath"; - - writer.writeStartElement(element); - writer.writeAttribute("name", en->title()); - QString docUrl = m_manifestDir + appendObsoleteToFileBase(en) + ".html"; - writer.writeAttribute("docUrl", docUrl); - const auto exampleFiles = en->files(); - if (en->projectFile().isEmpty()) - Location().warning("Example does not have a project file: ", en->name()); - else - writer.writeAttribute("projectPath", installPath + en->projectFile()); - if (en->imageFileName().isEmpty()) { - Location().warning("Example does not have an image file: ", en->name()); - } else { - writer.writeAttribute("imageUrl", m_manifestDir + en->imageFileName()); - usedAttributes << "imageUrl"; - } - - QString fullName = m_project + QLatin1Char('/') + en->title(); - QSet<QString> tags; - for (const auto &index : m_manifestMetaContent) { - const auto &names = index.names; - for (const QString &name : names) { - bool match = false; - int wildcard = name.indexOf(QChar('*')); - switch (wildcard) { - case -1: // no wildcard, exact match - match = (fullName == name); - break; - case 0: // '*' matches all - match = true; - break; - default: // match with wildcard at the end - match = fullName.startsWith(name.left(wildcard)); - } - if (match) { - tags += index.tags; - const auto attributes = index.attributes; - for (const QString &attr : attributes) { - QLatin1Char div(':'); - QStringList attrList = attr.split(div); - if (attrList.count() == 1) - attrList.append(QStringLiteral("true")); - QString attrName = attrList.takeFirst(); - if (!usedAttributes.contains(attrName)) { - writer.writeAttribute(attrName, attrList.join(div)); - usedAttributes << attrName; - } - } - } - } - } - - writer.writeStartElement("description"); - Text brief = en->doc().briefText(); - if (!brief.isEmpty()) - writer.writeCDATA(brief.toString()); - else - writer.writeCDATA(QString("No description available")); - writer.writeEndElement(); // description - - // Add words from module name as tags - // QtQuickControls -> qt,quick,controls - // QtOpenGL -> qt,opengl - QRegularExpression re("([A-Z]+[a-z0-9]*(3D|GL)?)"); - int pos = 0; - QRegularExpressionMatch match; - while ((match = re.match(m_project, pos)).hasMatch()) { - tags << match.captured(1).toLower(); - pos = match.capturedEnd(); - } - - // Include tags added via \meta {tag} {tag1[,tag2,...]} - // within \example topic - QStringMultiMap *metaTagMap = en->doc().metaTagMap(); - if (metaTagMap) { - for (const auto &tag : metaTagMap->values("tag")) { - const auto &tagList = tag.toLower().split(QLatin1Char(',')); - tags += QSet<QString>(tagList.cbegin(), tagList.cend()); - } - } - - const auto &titleWords = en->title().toLower().split(QLatin1Char(' ')); - tags += QSet<QString>(titleWords.cbegin(), titleWords.cend()); - - // Clean up tags, exclude invalid and common words - QSet<QString>::iterator tag_it = tags.begin(); - QSet<QString> modified; - while (tag_it != tags.end()) { - QString s = *tag_it; - if (s.at(0) == '(') - s.remove(0, 1).chop(1); - if (s.endsWith(QLatin1Char(':'))) - s.chop(1); - - if (s.length() < 2 || s.at(0).isDigit() || s.at(0) == '-' || s == QLatin1String("qt") - || s == QLatin1String("the") || s == QLatin1String("and") - || s.startsWith(QLatin1String("example")) || s.startsWith(QLatin1String("chapter"))) - tag_it = tags.erase(tag_it); - else if (s != *tag_it) { - modified << s; - tag_it = tags.erase(tag_it); - } else - ++tag_it; - } - tags += modified; - - if (!tags.isEmpty()) { - writer.writeStartElement("tags"); - bool wrote_one = false; - QStringList sortedTags = tags.values(); - sortedTags.sort(); - for (const auto &tag : qAsConst(sortedTags)) { - if (wrote_one) - writer.writeCharacters(","); - writer.writeCharacters(tag); - wrote_one = true; - } - writer.writeEndElement(); // tags - } - - QString ename = en->name().mid(en->name().lastIndexOf('/') + 1); - QMap<int, QString> filesToOpen; - const auto files = en->files(); - for (const QString &file : files) { - QFileInfo fileInfo(file); - QString fileName = fileInfo.fileName().toLower(); - // open .qml, .cpp and .h files with a - // basename matching the example (project) name - // QMap key indicates the priority - - // the lowest value will be the top-most file - if ((fileInfo.baseName().compare(ename, Qt::CaseInsensitive) == 0)) { - if (fileName.endsWith(".qml")) - filesToOpen.insert(0, file); - else if (fileName.endsWith(".cpp")) - filesToOpen.insert(1, file); - else if (fileName.endsWith(".h")) - filesToOpen.insert(2, file); - } - // main.qml takes precedence over main.cpp - else if (fileName.endsWith("main.qml")) { - filesToOpen.insert(3, file); - } else if (fileName.endsWith("main.cpp")) { - filesToOpen.insert(4, file); - } - } - - for (auto it = filesToOpen.constEnd(); it != filesToOpen.constBegin();) { - writer.writeStartElement("fileToOpen"); - if (--it == filesToOpen.constBegin()) { - writer.writeAttribute(QStringLiteral("mainFile"), QStringLiteral("true")); - } - writer.writeCharacters(installPath + it.value()); - writer.writeEndElement(); - } - - writer.writeEndElement(); // example - } - - writer.writeEndElement(); // examples - writer.writeEndElement(); // instructionals - writer.writeEndDocument(); - file.close(); -} - -/*! - Reads metacontent - additional attributes and tags to apply - when generating manifest files, read from config. Takes the - configuration class \a config as a parameter. - - The manifest metacontent map is cleared immediately after - the manifest files have been generated. - */ -void HtmlGenerator::readManifestMetaContent() -{ - Config &config = Config::instance(); - const QStringList names = - config.getStringList(CONFIG_MANIFESTMETA + Config::dot + QStringLiteral("filters")); - - for (const auto &manifest : names) { - ManifestMetaFilter filter; - QString prefix = CONFIG_MANIFESTMETA + Config::dot + manifest + Config::dot; - filter.names = config.getStringSet(prefix + QStringLiteral("names")); - filter.attributes = config.getStringSet(prefix + QStringLiteral("attributes")); - filter.tags = config.getStringSet(prefix + QStringLiteral("tags")); - m_manifestMetaContent.append(filter); - } -} - QT_END_NAMESPACE diff --git a/src/qdoc/htmlgenerator.h b/src/qdoc/htmlgenerator.h index 83a3248a7..07cc4c0d7 100644 --- a/src/qdoc/htmlgenerator.h +++ b/src/qdoc/htmlgenerator.h @@ -42,6 +42,7 @@ class Aggregate; class Config; class ExampleNode; class HelpProjectWriter; +class ManifestWriter; class HtmlGenerator : public XmlGenerator { @@ -53,7 +54,6 @@ public: void terminateGenerator() override; QString format() override; void generateDocs() override; - void generateManifestFiles(); QString protectEnc(const QString &string); static QString protect(const QString &string); @@ -69,9 +69,6 @@ protected: void generateCollectionNode(CollectionNode *cn, CodeMarker *marker) override; void generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) override; QString fileExtension() const override; - - void generateManifestFile(const QString &manifest, const QString &element); - void readManifestMetaContent(); void generateKeywordAnchors(const Node *node); private: @@ -85,7 +82,6 @@ private: QSet<QString> tags; }; - QString retrieveInstallPath(const ExampleNode *exampleNode); void generateNavigationBar(const QString &title, const Node *node, CodeMarker *marker, const QString &buildversion, bool tableItems = false); void generateHeader(const QString &title, const Node *node = nullptr, @@ -164,6 +160,7 @@ private: QString m_codePrefix {}; QString m_codeSuffix {}; HelpProjectWriter *m_helpProjectWriter { nullptr }; + ManifestWriter *m_manifestWriter { nullptr }; bool m_inObsoleteLink { false }; QRegularExpression m_funcLeftParen { "\\S(\\()" }; QString m_headerScripts {}; diff --git a/src/qdoc/manifestwriter.cpp b/src/qdoc/manifestwriter.cpp new file mode 100644 index 000000000..d19c6d6ff --- /dev/null +++ b/src/qdoc/manifestwriter.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $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 "manifestwriter.h" + +#include "config.h" +#include "examplenode.h" +#include "generator.h" +#include "qdocdatabase.h" + +#include <QtCore/qxmlstream.h> + +QT_BEGIN_NAMESPACE + +/*! + \class ManifestWriter + \internal + \brief The ManifestWriter is responsible for writing manifest files. + */ +ManifestWriter::ManifestWriter() +{ + Config &config = Config::instance(); + m_project = config.getString(CONFIG_PROJECT); + m_outputDirectory = config.getOutputDir(); + m_qdb = QDocDatabase::qdocDB(); + + const QString prefix = CONFIG_QHP + Config::dot + m_project + Config::dot; + m_manifestDir = + QLatin1String("qthelp://") + config.getString(prefix + QLatin1String("namespace")); + m_manifestDir += QLatin1Char('/') + config.getString(prefix + QLatin1String("virtualFolder")) + + QLatin1Char('/'); + readManifestMetaContent(); + m_examplesPath = config.getString(CONFIG_EXAMPLESINSTALLPATH); + if (!m_examplesPath.isEmpty()) + m_examplesPath += QLatin1Char('/'); +} + +/*! + This function outputs one or more manifest files in XML. + They are used by Creator. + */ +void ManifestWriter::generateManifestFiles() +{ + generateManifestFile("examples", "example"); + generateManifestFile("demos", "demo"); + m_qdb->exampleNodeMap().clear(); + m_manifestMetaContent.clear(); +} + +/*! + This function is called by generateManifestFiles(), once + for each manifest file to be generated. \a manifest is the + type of manifest file. + */ +void ManifestWriter::generateManifestFile(const QString &manifest, const QString &element) +{ + const ExampleNodeMap &exampleNodeMap = m_qdb->exampleNodeMap(); + if (exampleNodeMap.isEmpty()) + return; + const QString fileName = manifest + "-manifest.xml"; + QFile file(m_outputDirectory + QLatin1Char('/') + fileName); + bool demos = false; + if (manifest == QLatin1String("demos")) + demos = true; + + bool proceed = false; + for (auto map = exampleNodeMap.begin(); map != exampleNodeMap.end(); ++map) { + const ExampleNode *en = map.value(); + if (demos == en->name().startsWith("demos")) { + proceed = true; + break; + } + } + if (!proceed || !file.open(QFile::WriteOnly | QFile::Text)) + return; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("instructionals"); + writer.writeAttribute("module", m_project); + writer.writeStartElement(manifest); + + QStringList usedAttributes; + for (auto map = exampleNodeMap.begin(); map != exampleNodeMap.end(); ++map) { + const ExampleNode *en = map.value(); + if (demos) { + if (!en->name().startsWith("demos")) + continue; + } else if (en->name().startsWith("demos")) { + continue; + } + + const QString installPath = retrieveInstallPath(en); + // attributes that are always written for the element + usedAttributes.clear(); + usedAttributes << "name" + << "docUrl" + << "projectPath"; + + writer.writeStartElement(element); + writer.writeAttribute("name", en->title()); + QString docUrl = m_manifestDir + Generator::fileBase(en) + ".html"; + writer.writeAttribute("docUrl", docUrl); + const auto exampleFiles = en->files(); + if (en->projectFile().isEmpty()) + Location().warning("Example does not have a project file: ", en->name()); + else + writer.writeAttribute("projectPath", installPath + en->projectFile()); + if (en->imageFileName().isEmpty()) { + Location().warning("Example does not have an image file: ", en->name()); + } else { + writer.writeAttribute("imageUrl", m_manifestDir + en->imageFileName()); + usedAttributes << "imageUrl"; + } + + QString fullName = m_project + QLatin1Char('/') + en->title(); + QSet<QString> tags; + for (const auto &index : m_manifestMetaContent) { + const auto &names = index.names; + for (const QString &name : names) { + bool match; + int wildcard = name.indexOf(QChar('*')); + switch (wildcard) { + case -1: // no wildcard, exact match + match = (fullName == name); + break; + case 0: // '*' matches all + match = true; + break; + default: // match with wildcard at the end + match = fullName.startsWith(name.left(wildcard)); + } + if (match) { + tags += index.tags; + const auto attributes = index.attributes; + for (const QString &attr : attributes) { + QLatin1Char div(':'); + QStringList attrList = attr.split(div); + if (attrList.count() == 1) + attrList.append(QStringLiteral("true")); + QString attrName = attrList.takeFirst(); + if (!usedAttributes.contains(attrName)) { + writer.writeAttribute(attrName, attrList.join(div)); + usedAttributes << attrName; + } + } + } + } + } + + writer.writeStartElement("description"); + Text brief = en->doc().briefText(); + if (!brief.isEmpty()) + writer.writeCDATA(brief.toString()); + else + writer.writeCDATA(QString("No description available")); + writer.writeEndElement(); // description + + // Add words from module name as tags + // QtQuickControls -> qt,quick,controls + // QtOpenGL -> qt,opengl + QRegularExpression re("([A-Z]+[a-z0-9]*(3D|GL)?)"); + int pos = 0; + QRegularExpressionMatch match; + while ((match = re.match(m_project, pos)).hasMatch()) { + tags << match.captured(1).toLower(); + pos = match.capturedEnd(); + } + + // Include tags added via \meta {tag} {tag1[,tag2,...]} + // within \example topic + const QStringMultiMap *metaTagMap = en->doc().metaTagMap(); + if (metaTagMap) { + for (const auto &tag : metaTagMap->values("tag")) { + const auto &tagList = tag.toLower().split(QLatin1Char(',')); + tags += QSet<QString>(tagList.cbegin(), tagList.cend()); + } + } + + const auto &titleWords = en->title().toLower().split(QLatin1Char(' ')); + tags += QSet<QString>(titleWords.cbegin(), titleWords.cend()); + + // Clean up tags, exclude invalid and common words + QSet<QString>::iterator tag_it = tags.begin(); + QSet<QString> modified; + while (tag_it != tags.end()) { + QString s = *tag_it; + if (s.at(0) == '(') + s.remove(0, 1).chop(1); + if (s.endsWith(QLatin1Char(':'))) + s.chop(1); + + if (s.length() < 2 || s.at(0).isDigit() || s.at(0) == '-' || s == QLatin1String("qt") + || s == QLatin1String("the") || s == QLatin1String("and") + || s.startsWith(QLatin1String("example")) || s.startsWith(QLatin1String("chapter"))) + tag_it = tags.erase(tag_it); + else if (s != *tag_it) { + modified << s; + tag_it = tags.erase(tag_it); + } else + ++tag_it; + } + tags += modified; + + if (!tags.isEmpty()) { + writer.writeStartElement("tags"); + bool wrote_one = false; + QStringList sortedTags = tags.values(); + sortedTags.sort(); + for (const auto &tag : qAsConst(sortedTags)) { + if (wrote_one) + writer.writeCharacters(","); + writer.writeCharacters(tag); + wrote_one = true; + } + writer.writeEndElement(); // tags + } + + QString exampleName = en->name().mid(en->name().lastIndexOf('/') + 1); + QMap<int, QString> filesToOpen; + const auto files = en->files(); + for (const QString &file : files) { + QFileInfo fileInfo(file); + QString fileName = fileInfo.fileName().toLower(); + // open .qml, .cpp and .h files with a + // basename matching the example (project) name + // QMap key indicates the priority - + // the lowest value will be the top-most file + if ((fileInfo.baseName().compare(exampleName, Qt::CaseInsensitive) == 0)) { + if (fileName.endsWith(".qml")) + filesToOpen.insert(0, file); + else if (fileName.endsWith(".cpp")) + filesToOpen.insert(1, file); + else if (fileName.endsWith(".h")) + filesToOpen.insert(2, file); + } + // main.qml takes precedence over main.cpp + else if (fileName.endsWith("main.qml")) { + filesToOpen.insert(3, file); + } else if (fileName.endsWith("main.cpp")) { + filesToOpen.insert(4, file); + } + } + + for (auto it = filesToOpen.constEnd(); it != filesToOpen.constBegin();) { + writer.writeStartElement("fileToOpen"); + if (--it == filesToOpen.constBegin()) { + writer.writeAttribute(QStringLiteral("mainFile"), QStringLiteral("true")); + } + writer.writeCharacters(installPath + it.value()); + writer.writeEndElement(); + } + + writer.writeEndElement(); // example + } + + writer.writeEndElement(); // examples + writer.writeEndElement(); // instructionals + writer.writeEndDocument(); + file.close(); +} + +/*! + Reads metacontent - additional attributes and tags to apply + when generating manifest files, read from config. + + The manifest metacontent map is cleared immediately after + the manifest files have been generated. + */ +void ManifestWriter::readManifestMetaContent() +{ + Config &config = Config::instance(); + const QStringList names = + config.getStringList(CONFIG_MANIFESTMETA + Config::dot + QStringLiteral("filters")); + + for (const auto &manifest : names) { + ManifestMetaFilter filter; + QString prefix = CONFIG_MANIFESTMETA + Config::dot + manifest + Config::dot; + filter.names = config.getStringSet(prefix + QStringLiteral("names")); + filter.attributes = config.getStringSet(prefix + QStringLiteral("attributes")); + filter.tags = config.getStringSet(prefix + QStringLiteral("tags")); + m_manifestMetaContent.append(filter); + } +} + +/*! + Retrieve the install path for the \a example as specified with + the \meta command, or fall back to the one defined in .qdocconf. + */ +QString ManifestWriter::retrieveInstallPath(const ExampleNode *example) +{ + QString installPath; + if (example->doc().metaTagMap()) + installPath = example->doc().metaTagMap()->value(QLatin1String("installpath")); + if (installPath.isEmpty()) + installPath = m_examplesPath; + if (!installPath.isEmpty() && !installPath.endsWith(QLatin1Char('/'))) + installPath += QLatin1Char('/'); + + return installPath; +} + +QT_END_NAMESPACE diff --git a/src/qdoc/manifestwriter.h b/src/qdoc/manifestwriter.h new file mode 100644 index 000000000..907a7742c --- /dev/null +++ b/src/qdoc/manifestwriter.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $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 MANIFESTWRITER_H +#define MANIFESTWRITER_H + +#include <QtCore/qlist.h> +#include <QtCore/qset.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class ExampleNode; +class QDocDatabase; + +class ManifestWriter +{ + struct ManifestMetaFilter + { + QSet<QString> names; + QSet<QString> attributes; + QSet<QString> tags; + }; + +public: + ManifestWriter(); + void generateManifestFiles(); + void generateManifestFile(const QString &manifest, const QString &element); + void readManifestMetaContent(); + QString retrieveInstallPath(const ExampleNode *example); + +private: + QString m_manifestDir {}; + QString m_examplesPath {}; + QString m_outputDirectory {}; + QString m_project {}; + QDocDatabase *m_qdb { nullptr }; + QList<ManifestMetaFilter> m_manifestMetaContent {}; +}; + +QT_END_NAMESPACE + +#endif // MANIFESTWRITER_H diff --git a/src/qdoc/qdoc.pro b/src/qdoc/qdoc.pro index a1f47300d..a6a31eb77 100644 --- a/src/qdoc/qdoc.pro +++ b/src/qdoc/qdoc.pro @@ -62,6 +62,7 @@ HEADERS += access.h \ location.h \ loggingcategory.h \ macro.h \ + manifestwriter.h \ namespacenode.h \ node.h \ openedlist.h \ @@ -118,6 +119,7 @@ SOURCES += aggregate.cpp \ htmlgenerator.cpp \ location.cpp \ main.cpp \ + manifestwriter.cpp \ namespacenode.cpp \ node.cpp \ openedlist.cpp \ |