diff options
Diffstat (limited to 'src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp')
-rw-r--r-- | src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp | 903 |
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 |