summaryrefslogtreecommitdiffstats
path: root/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp')
-rw-r--r--src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp903
1 files changed, 903 insertions, 0 deletions
diff --git a/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp
new file mode 100644
index 000000000..c2cc38161
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp
@@ -0,0 +1,903 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "webxmlgenerator.h"
+
+#include "aggregate.h"
+#include "collectionnode.h"
+#include "config.h"
+#include "helpprojectwriter.h"
+#include "node.h"
+#include "propertynode.h"
+#include "qdocdatabase.h"
+#include "quoter.h"
+#include "utilities.h"
+
+#include <QtCore/qxmlstream.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static CodeMarker *marker_ = nullptr;
+
+WebXMLGenerator::WebXMLGenerator(FileResolver& file_resolver) : HtmlGenerator(file_resolver) {}
+
+void WebXMLGenerator::initializeGenerator()
+{
+ HtmlGenerator::initializeGenerator();
+}
+
+void WebXMLGenerator::terminateGenerator()
+{
+ HtmlGenerator::terminateGenerator();
+}
+
+QString WebXMLGenerator::format()
+{
+ return "WebXML";
+}
+
+QString WebXMLGenerator::fileExtension() const
+{
+ // As this is meant to be an intermediate format,
+ // use .html for internal references. The name of
+ // the output file is set separately in
+ // beginSubPage() calls.
+ return "html";
+}
+
+/*!
+ Most of the output is generated by QDocIndexFiles and the append() callback.
+ Some pages produce supplementary output while being generated, and that's
+ handled here.
+*/
+qsizetype WebXMLGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ if (m_supplement && currentWriter)
+ addAtomElements(*currentWriter.data(), atom, relative, marker);
+ return 0;
+}
+
+void WebXMLGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker * /* marker */)
+{
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ beginSubPage(aggregate, Generator::fileName(aggregate, "webxml"));
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+
+ generateIndexSections(writer, aggregate);
+
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ endSubPage();
+}
+
+void WebXMLGenerator::generatePageNode(PageNode *pn, CodeMarker * /* marker */)
+{
+ QByteArray data;
+ currentWriter.reset(new QXmlStreamWriter(&data));
+ currentWriter->setAutoFormatting(true);
+ beginSubPage(pn, Generator::fileName(pn, "webxml"));
+ currentWriter->writeStartDocument();
+ currentWriter->writeStartElement("WebXML");
+ currentWriter->writeStartElement("document");
+
+ generateIndexSections(*currentWriter.data(), pn);
+
+ currentWriter->writeEndElement(); // document
+ currentWriter->writeEndElement(); // WebXML
+ currentWriter->writeEndDocument();
+
+ out() << data;
+ endSubPage();
+}
+
+void WebXMLGenerator::generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker* /* marker */)
+{
+ // TODO: [generator-insufficient-structural-abstraction]
+
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ beginSubPage(en, linkForExampleFile(resolved_file.get_query(), "webxml"));
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+ writer.writeStartElement("page");
+ writer.writeAttribute("name", resolved_file.get_query());
+ writer.writeAttribute("href", linkForExampleFile(resolved_file.get_query()));
+ const QString title = exampleFileTitle(static_cast<const ExampleNode *>(en), resolved_file.get_query());
+ writer.writeAttribute("title", title);
+ writer.writeAttribute("fulltitle", title);
+ writer.writeAttribute("subtitle", resolved_file.get_query());
+ writer.writeStartElement("description");
+
+ if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
+ writer.writeAttribute("path", resolved_file.get_path());
+ writer.writeAttribute("line", "0");
+ writer.writeAttribute("column", "0");
+ }
+
+ Quoter quoter;
+ Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
+ QString code = quoter.quoteTo(en->location(), QString(), QString());
+ writer.writeTextElement("code", trimmedTrailing(code, QString(), QString()));
+
+ writer.writeEndElement(); // description
+ writer.writeEndElement(); // page
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ endSubPage();
+}
+
+void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, Node *node)
+{
+ marker_ = CodeMarker::markerForFileName(node->location().filePath());
+ auto qdocIndexFiles = QDocIndexFiles::qdocIndexFiles();
+ if (qdocIndexFiles) {
+ qdocIndexFiles->generateIndexSections(writer, node, this);
+ // generateIndexSections does nothing for groups, so handle them explicitly
+ if (node->isGroup())
+ qdocIndexFiles->generateIndexSection(writer, node, this);
+ }
+}
+
+// Handles callbacks from QDocIndexFiles to add documentation to node
+void WebXMLGenerator::append(QXmlStreamWriter &writer, Node *node)
+{
+ Q_ASSERT(marker_);
+
+ writer.writeStartElement("description");
+ if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
+ writer.writeAttribute("path", node->doc().location().filePath());
+ writer.writeAttribute("line", QString::number(node->doc().location().lineNo()));
+ writer.writeAttribute("column", QString::number(node->doc().location().columnNo()));
+ }
+
+ if (node->isTextPageNode())
+ generateRelations(writer, node);
+
+ if (node->isModule()) {
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", "classesbymodule");
+ auto *cnn = static_cast<CollectionNode *>(node);
+
+ if (cnn->hasNamespaces()) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Namespaces");
+ writer.writeEndElement(); // heading
+ NodeMap namespaces{cnn->getMembers(Node::Namespace)};
+ generateAnnotatedList(writer, node, namespaces);
+ writer.writeEndElement(); // section
+ }
+ if (cnn->hasClasses()) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Classes");
+ writer.writeEndElement(); // heading
+ NodeMap classes{cnn->getMembers([](const Node *n){ return n->isClassNode(); })};
+ generateAnnotatedList(writer, node, classes);
+ writer.writeEndElement(); // section
+ }
+ writer.writeEndElement(); // generatedlist
+ }
+
+ m_inLink = m_inSectionHeading = m_hasQuotingInformation = false;
+
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom)
+ atom = addAtomElements(writer, atom, node, marker_);
+
+ QList<Text> alsoList = node->doc().alsoList();
+ supplementAlsoList(node, alsoList);
+
+ if (!alsoList.isEmpty()) {
+ writer.writeStartElement("see-also");
+ for (const auto &item : alsoList) {
+ const auto *atom = item.firstAtom();
+ while (atom)
+ atom = addAtomElements(writer, atom, node, marker_);
+ }
+ writer.writeEndElement(); // see-also
+ }
+
+ if (node->isExample()) {
+ m_supplement = true;
+ generateRequiredLinks(node, marker_);
+ m_supplement = false;
+ } else if (node->isGroup()) {
+ auto *cn = static_cast<CollectionNode *>(node);
+ if (!cn->noAutoList())
+ generateAnnotatedList(writer, node, cn->members());
+ }
+
+ writer.writeEndElement(); // description
+}
+
+void WebXMLGenerator::generateDocumentation(Node *node)
+{
+ // Don't generate nodes that are already processed, or if they're not supposed to
+ // generate output, ie. external, index or images nodes.
+ if (!node->url().isNull() || node->isExternalPage() || node->isIndexNode())
+ return;
+
+ if (node->isInternal() && !m_showInternal)
+ return;
+
+ if (node->parent()) {
+ if (node->isNamespace() || node->isClassNode() || node->isHeader())
+ generateCppReferencePage(static_cast<Aggregate *>(node), nullptr);
+ else if (node->isCollectionNode()) {
+ if (node->wasSeen()) {
+ // see remarks in base class impl.
+ m_qdb->mergeCollections(static_cast<CollectionNode *>(node));
+ generatePageNode(static_cast<PageNode *>(node), nullptr);
+ }
+ } else if (node->isTextPageNode())
+ generatePageNode(static_cast<PageNode *>(node), nullptr);
+ // else if TODO: anything else?
+ }
+
+ if (node->isAggregate()) {
+ auto *aggregate = static_cast<Aggregate *>(node);
+ for (auto c : aggregate->childNodes()) {
+ if ((c->isAggregate() || c->isTextPageNode() || c->isCollectionNode())
+ && !c->isPrivate())
+ generateDocumentation(c);
+ }
+ }
+}
+
+const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *relative, CodeMarker *marker)
+{
+ bool keepQuoting = false;
+
+ if (!atom)
+ return nullptr;
+
+ switch (atom->type()) {
+ case Atom::AnnotatedList: {
+ const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), Node::Group);
+ if (cn)
+ generateAnnotatedList(writer, relative, cn->members());
+ } break;
+ case Atom::AutoLink: {
+ const Node *node{nullptr};
+ QString link{};
+
+ if (!m_inLink && !m_inSectionHeading) {
+ link = getAutoLink(atom, relative, &node, Node::API);
+
+ if (!link.isEmpty() && node && node->isDeprecated()
+ && relative->parent() != node && !relative->isDeprecated()) {
+ link.clear();
+ }
+ }
+
+ startLink(writer, atom, node, link);
+
+ writer.writeCharacters(atom->string());
+
+ if (m_inLink) {
+ writer.writeEndElement(); // link
+ m_inLink = false;
+ }
+
+ break;
+ }
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+
+ writer.writeStartElement("brief");
+ switch (relative->nodeType()) {
+ case Node::Property:
+ writer.writeCharacters("This property");
+ break;
+ case Node::Variable:
+ writer.writeCharacters("This variable");
+ break;
+ default:
+ break;
+ }
+ if (relative->isProperty() || relative->isVariable()) {
+ QString str;
+ const Atom *a = atom->next();
+ while (a != nullptr && a->type() != Atom::BriefRight) {
+ if (a->type() == Atom::String || a->type() == Atom::AutoLink)
+ str += a->string();
+ a = a->next();
+ }
+ str[0] = str[0].toLower();
+ if (str.endsWith('.'))
+ str.chop(1);
+
+ const QList<QStringView> words = QStringView{str}.split(' ');
+ if (!words.isEmpty()) {
+ QStringView first(words.at(0));
+ if (!(first == u"contains" || first == u"specifies" || first == u"describes"
+ || first == u"defines" || first == u"holds" || first == u"determines"))
+ writer.writeCharacters(" holds ");
+ else
+ writer.writeCharacters(" ");
+ }
+ }
+ break;
+
+ case Atom::BriefRight:
+ if (relative->isProperty() || relative->isVariable())
+ writer.writeCharacters(".");
+
+ writer.writeEndElement(); // brief
+ break;
+
+ case Atom::C:
+ writer.writeStartElement("teletype");
+ if (m_inLink)
+ writer.writeAttribute("type", "normal");
+ else
+ writer.writeAttribute("type", "highlighted");
+
+ writer.writeCharacters(plainCode(atom->string()));
+ writer.writeEndElement(); // teletype
+ break;
+
+ case Atom::Code:
+ if (!m_hasQuotingInformation)
+ writer.writeTextElement(
+ "code", trimmedTrailing(plainCode(atom->string()), QString(), QString()));
+ else
+ keepQuoting = true;
+ break;
+
+ case Atom::CodeBad:
+ writer.writeTextElement("badcode",
+ trimmedTrailing(plainCode(atom->string()), QString(), QString()));
+ break;
+
+ case Atom::CodeQuoteArgument:
+ if (m_quoting) {
+ if (quoteCommand == "dots") {
+ writer.writeAttribute("indent", atom->string());
+ writer.writeCharacters("...");
+ } else {
+ writer.writeCharacters(atom->string());
+ }
+ writer.writeEndElement(); // code
+ keepQuoting = true;
+ }
+ break;
+
+ case Atom::CodeQuoteCommand:
+ if (m_quoting) {
+ quoteCommand = atom->string();
+ writer.writeStartElement(quoteCommand);
+ }
+ break;
+
+ case Atom::ExampleFileLink: {
+ if (!m_inLink) {
+ QString link = linkForExampleFile(atom->string());
+ if (!link.isEmpty())
+ startLink(writer, atom, relative, link);
+ }
+ } break;
+
+ case Atom::ExampleImageLink: {
+ if (!m_inLink) {
+ QString link = atom->string();
+ if (!link.isEmpty())
+ startLink(writer, atom, nullptr, "images/used-in-examples/" + link);
+ }
+ } break;
+
+ case Atom::FootnoteLeft:
+ writer.writeStartElement("footnote");
+ break;
+
+ case Atom::FootnoteRight:
+ writer.writeEndElement(); // footnote
+ break;
+
+ case Atom::FormatEndif:
+ writer.writeEndElement(); // raw
+ break;
+ case Atom::FormatIf:
+ writer.writeStartElement("raw");
+ writer.writeAttribute("format", atom->string());
+ break;
+ case Atom::FormattingLeft: {
+ if (atom->string() == ATOM_FORMATTING_BOLD)
+ writer.writeStartElement("bold");
+ else if (atom->string() == ATOM_FORMATTING_ITALIC)
+ writer.writeStartElement("italic");
+ else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
+ writer.writeStartElement("underline");
+ else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
+ writer.writeStartElement("subscript");
+ else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
+ writer.writeStartElement("superscript");
+ else if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ writer.writeStartElement("teletype");
+ else if (atom->string() == ATOM_FORMATTING_PARAMETER)
+ writer.writeStartElement("argument");
+ else if (atom->string() == ATOM_FORMATTING_INDEX)
+ writer.writeStartElement("index");
+ } break;
+
+ case Atom::FormattingRight: {
+ if (atom->string() == ATOM_FORMATTING_BOLD)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_ITALIC)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_PARAMETER)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_INDEX)
+ writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_TRADEMARK && appendTrademark(atom))
+ writer.writeCharacters(QChar(0x2122)); // 'TM' symbol
+ }
+ if (m_inLink) {
+ writer.writeEndElement(); // link
+ m_inLink = false;
+ }
+ break;
+
+ case Atom::GeneratedList:
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", atom->string());
+ writer.writeEndElement();
+ break;
+
+ // TODO: The other generators treat inlineimage and image
+ // simultaneously as the diffirences aren't big. It should be
+ // possible to do the same for webxmlgenerator instead of
+ // repeating the code.
+
+ // TODO: [generator-insufficient-structural-abstraction]
+ case Atom::Image: {
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
+ } else {
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ writer.writeStartElement("image");
+ // TODO: [uncentralized-output-directory-structure]
+ writer.writeAttribute("href", "images/" + file_name);
+ writer.writeEndElement();
+ // TODO: [uncentralized-output-directory-structure]
+ setImageFileName(relative, "images/" + file_name);
+ }
+ break;
+ }
+ // TODO: [generator-insufficient-structural-abstraction]
+ case Atom::InlineImage: {
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
+ } else {
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ writer.writeStartElement("inlineimage");
+ // TODO: [uncentralized-output-directory-structure]
+ writer.writeAttribute("href", "images/" + file_name);
+ writer.writeEndElement();
+ // TODO: [uncentralized-output-directory-structure]
+ setImageFileName(relative, "images/" + file_name);
+ }
+ break;
+ }
+ case Atom::ImageText:
+ break;
+
+ case Atom::ImportantLeft:
+ writer.writeStartElement("para");
+ writer.writeTextElement("bold", "Important:");
+ writer.writeCharacters(" ");
+ break;
+
+ case Atom::LegaleseLeft:
+ writer.writeStartElement("legalese");
+ break;
+
+ case Atom::LegaleseRight:
+ writer.writeEndElement(); // legalese
+ break;
+
+ case Atom::Link:
+ case Atom::LinkNode:
+ if (!m_inLink) {
+ const Node *node = nullptr;
+ QString link = getLink(atom, relative, &node);
+ if (!link.isEmpty())
+ startLink(writer, atom, node, link);
+ }
+ break;
+
+ case Atom::ListLeft:
+ writer.writeStartElement("list");
+
+ if (atom->string() == ATOM_LIST_BULLET)
+ writer.writeAttribute("type", "bullet");
+ else if (atom->string() == ATOM_LIST_TAG)
+ writer.writeAttribute("type", "definition");
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ if (relative->isEnumType())
+ writer.writeAttribute("type", "enum");
+ else
+ writer.writeAttribute("type", "definition");
+ } else {
+ writer.writeAttribute("type", "ordered");
+ if (atom->string() == ATOM_LIST_UPPERALPHA)
+ writer.writeAttribute("start", "A");
+ else if (atom->string() == ATOM_LIST_LOWERALPHA)
+ writer.writeAttribute("start", "a");
+ else if (atom->string() == ATOM_LIST_UPPERROMAN)
+ writer.writeAttribute("start", "I");
+ else if (atom->string() == ATOM_LIST_LOWERROMAN)
+ writer.writeAttribute("start", "i");
+ else // (atom->string() == ATOM_LIST_NUMERIC)
+ writer.writeAttribute("start", "1");
+ }
+ break;
+
+ case Atom::ListItemNumber:
+ break;
+ case Atom::ListTagLeft: {
+ writer.writeStartElement("definition");
+
+ writer.writeTextElement(
+ "term", plainCode(marker->markedUpEnumValue(atom->next()->string(), relative)));
+ } break;
+
+ case Atom::ListTagRight:
+ writer.writeEndElement(); // definition
+ break;
+
+ case Atom::ListItemLeft:
+ writer.writeStartElement("item");
+ break;
+
+ case Atom::ListItemRight:
+ writer.writeEndElement(); // item
+ break;
+
+ case Atom::ListRight:
+ writer.writeEndElement(); // list
+ break;
+
+ case Atom::NoteLeft:
+ writer.writeStartElement("para");
+ writer.writeTextElement("bold", "Note:");
+ writer.writeCharacters(" ");
+ break;
+
+ // End admonition elements
+ case Atom::ImportantRight:
+ case Atom::NoteRight:
+ case Atom::WarningRight:
+ writer.writeEndElement(); // para
+ break;
+
+ case Atom::Nop:
+ break;
+
+ case Atom::CaptionLeft:
+ case Atom::ParaLeft:
+ writer.writeStartElement("para");
+ break;
+
+ case Atom::CaptionRight:
+ case Atom::ParaRight:
+ writer.writeEndElement(); // para
+ break;
+
+ case Atom::QuotationLeft:
+ writer.writeStartElement("quote");
+ break;
+
+ case Atom::QuotationRight:
+ writer.writeEndElement(); // quote
+ break;
+
+ case Atom::RawString:
+ writer.writeCharacters(atom->string());
+ break;
+
+ case Atom::SectionLeft:
+ writer.writeStartElement("section");
+ writer.writeAttribute("id",
+ Utilities::asAsciiPrintable(Text::sectionHeading(atom).toString()));
+ break;
+
+ case Atom::SectionRight:
+ writer.writeEndElement(); // section
+ break;
+
+ case Atom::SectionHeadingLeft: {
+ writer.writeStartElement("heading");
+ int unit = atom->string().toInt(); // + hOffset(relative)
+ writer.writeAttribute("level", QString::number(unit));
+ m_inSectionHeading = true;
+ } break;
+
+ case Atom::SectionHeadingRight:
+ writer.writeEndElement(); // heading
+ m_inSectionHeading = false;
+ break;
+
+ case Atom::SidebarLeft:
+ case Atom::SidebarRight:
+ break;
+
+ case Atom::SnippetCommand:
+ if (m_quoting) {
+ writer.writeStartElement(atom->string());
+ }
+ break;
+
+ case Atom::SnippetIdentifier:
+ if (m_quoting) {
+ writer.writeAttribute("identifier", atom->string());
+ writer.writeEndElement();
+ keepQuoting = true;
+ }
+ break;
+
+ case Atom::SnippetLocation:
+ if (m_quoting) {
+ const QString &location = atom->string();
+ writer.writeAttribute("location", location);
+ auto maybe_resolved_file{file_resolver.resolve(location)};
+ // const QString resolved = Doc::resolveFile(Location(), location);
+ if (maybe_resolved_file)
+ writer.writeAttribute("path", (*maybe_resolved_file).get_path());
+ else {
+ // TODO: [uncetnralized-admonition][failed-resolve-file]
+ QString details = std::transform_reduce(
+ file_resolver.get_search_directories().cbegin(),
+ file_resolver.get_search_directories().cend(),
+ u"Searched directories:"_s,
+ std::plus(),
+ [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
+ );
+
+ relative->location().warning(u"Cannot find file to quote from: %1"_s.arg(location), details);
+ }
+ }
+ break;
+
+ case Atom::String:
+ writer.writeCharacters(atom->string());
+ break;
+ case Atom::TableLeft:
+ writer.writeStartElement("table");
+ if (atom->string().contains("%"))
+ writer.writeAttribute("width", atom->string());
+ break;
+
+ case Atom::TableRight:
+ writer.writeEndElement(); // table
+ break;
+
+ case Atom::TableHeaderLeft:
+ writer.writeStartElement("header");
+ break;
+
+ case Atom::TableHeaderRight:
+ writer.writeEndElement(); // header
+ break;
+
+ case Atom::TableRowLeft:
+ writer.writeStartElement("row");
+ break;
+
+ case Atom::TableRowRight:
+ writer.writeEndElement(); // row
+ break;
+
+ case Atom::TableItemLeft: {
+ writer.writeStartElement("item");
+ QStringList spans = atom->string().split(",");
+ if (spans.size() == 2) {
+ if (spans.at(0) != "1")
+ writer.writeAttribute("colspan", spans.at(0).trimmed());
+ if (spans.at(1) != "1")
+ writer.writeAttribute("rowspan", spans.at(1).trimmed());
+ }
+ } break;
+ case Atom::TableItemRight:
+ writer.writeEndElement(); // item
+ break;
+
+ case Atom::Target:
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", Utilities::asAsciiPrintable(atom->string()));
+ writer.writeEndElement();
+ break;
+
+ case Atom::WarningLeft:
+ writer.writeStartElement("para");
+ writer.writeTextElement("bold", "Warning:");
+ writer.writeCharacters(" ");
+ break;
+
+ case Atom::UnhandledFormat:
+ case Atom::UnknownCommand:
+ writer.writeCharacters(atom->typeString());
+ break;
+ default:
+ break;
+ }
+
+ m_hasQuotingInformation = keepQuoting;
+ return atom->next();
+}
+
+void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom, const Node *node,
+ const QString &link)
+{
+ QString fullName = link;
+ if (node)
+ fullName = node->fullName();
+ if (!fullName.isEmpty() && !link.isEmpty()) {
+ writer.writeStartElement("link");
+ if (atom && !atom->string().isEmpty())
+ writer.writeAttribute("raw", atom->string());
+ else
+ writer.writeAttribute("raw", fullName);
+ writer.writeAttribute("href", link);
+ writer.writeAttribute("type", targetType(node));
+ if (node) {
+ switch (node->nodeType()) {
+ case Node::Enum:
+ writer.writeAttribute("enum", fullName);
+ break;
+ case Node::Example: {
+ const auto *en = static_cast<const ExampleNode *>(node);
+ const QString fileTitle = atom ? exampleFileTitle(en, atom->string()) : QString();
+ if (!fileTitle.isEmpty()) {
+ writer.writeAttribute("page", fileTitle);
+ break;
+ }
+ }
+ Q_FALLTHROUGH();
+ case Node::Page:
+ writer.writeAttribute("page", fullName);
+ break;
+ case Node::Property: {
+ const auto *propertyNode = static_cast<const PropertyNode *>(node);
+ if (!propertyNode->getters().empty())
+ writer.writeAttribute("getter", propertyNode->getters().at(0)->fullName());
+ } break;
+ default:
+ break;
+ }
+ }
+ m_inLink = true;
+ }
+}
+
+void WebXMLGenerator::endLink(QXmlStreamWriter &writer)
+{
+ if (m_inLink) {
+ writer.writeEndElement(); // link
+ m_inLink = false;
+ }
+}
+
+void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node)
+{
+ if (node && !node->links().empty()) {
+ std::pair<QString, QString> anchorPair;
+ const Node *linkNode;
+
+ for (auto it = node->links().cbegin(); it != node->links().cend(); ++it) {
+
+ linkNode = m_qdb->findNodeForTarget(it.value().first, node);
+
+ if (!linkNode)
+ linkNode = node;
+
+ if (linkNode == node)
+ anchorPair = it.value();
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ writer.writeStartElement("relation");
+ writer.writeAttribute("href", anchorPair.first);
+ writer.writeAttribute("type", targetType(linkNode));
+
+ switch (it.key()) {
+ case Node::StartLink:
+ writer.writeAttribute("meta", "start");
+ break;
+ case Node::NextLink:
+ writer.writeAttribute("meta", "next");
+ break;
+ case Node::PreviousLink:
+ writer.writeAttribute("meta", "previous");
+ break;
+ case Node::ContentsLink:
+ writer.writeAttribute("meta", "contents");
+ break;
+ default:
+ writer.writeAttribute("meta", "");
+ }
+ writer.writeAttribute("description", anchorPair.second);
+ writer.writeEndElement(); // link
+ }
+ }
+}
+
+void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative,
+ const NodeMap &nodeMap)
+{
+ generateAnnotatedList(writer, relative, nodeMap.values());
+}
+
+void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative,
+ const NodeList &nodeList)
+{
+ writer.writeStartElement("table");
+ writer.writeAttribute("width", "100%");
+
+ for (const auto *node : nodeList) {
+ writer.writeStartElement("row");
+ writer.writeStartElement("item");
+ writer.writeStartElement("para");
+ const QString link = linkForNode(node, relative);
+ startLink(writer, node->doc().body().firstAtom(), node, link);
+ endLink(writer);
+ writer.writeEndElement(); // para
+ writer.writeEndElement(); // item
+
+ writer.writeStartElement("item");
+ writer.writeStartElement("para");
+ writer.writeCharacters(node->doc().briefText().toString());
+ writer.writeEndElement(); // para
+ writer.writeEndElement(); // item
+ writer.writeEndElement(); // row
+ }
+ writer.writeEndElement(); // table
+}
+
+QString WebXMLGenerator::fileBase(const Node *node) const
+{
+ return Generator::fileBase(node);
+}
+
+QT_END_NAMESPACE