| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 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 "webxmlgenerator.h" |
| |
| #include "config.h" |
| #include "helpprojectwriter.h" |
| #include "node.h" |
| #include "qdocdatabase.h" |
| #include "separator.h" |
| #include "quoter.h" |
| #include "tree.h" |
| |
| #include <QtCore/qxmlstream.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static CodeMarker *marker_ = nullptr; |
| |
| void WebXMLGenerator::initializeGenerator() |
| { |
| HtmlGenerator::initializeGenerator(); |
| } |
| |
| void WebXMLGenerator::terminateGenerator() |
| { |
| Generator::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. |
| */ |
| int WebXMLGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) |
| { |
| if (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, const QString &file, |
| CodeMarker * /* marker */) |
| { |
| QByteArray data; |
| QXmlStreamWriter writer(&data); |
| writer.setAutoFormatting(true); |
| beginFilePage(en, linkForExampleFile(file, en, "webxml")); |
| writer.writeStartDocument(); |
| writer.writeStartElement("WebXML"); |
| writer.writeStartElement("document"); |
| writer.writeStartElement("page"); |
| writer.writeAttribute("name", file); |
| writer.writeAttribute("href", linkForExampleFile(file, en)); |
| QString title = exampleFileTitle(static_cast<const ExampleNode *>(en), file); |
| writer.writeAttribute("title", title); |
| writer.writeAttribute("fulltitle", title); |
| writer.writeAttribute("subtitle", file); |
| writer.writeStartElement("description"); |
| |
| if (Config::instance().getBool(CONFIG_LOCATIONINFO)) { |
| QString userFriendlyFilePath; // unused |
| writer.writeAttribute("path", |
| Doc::resolveFile(en->doc().location(), file, &userFriendlyFilePath)); |
| writer.writeAttribute("line", "0"); |
| writer.writeAttribute("column", "0"); |
| } |
| |
| Quoter quoter; |
| Doc::quoteFromFile(en->doc().location(), quoter, 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; |
| endFilePage(); |
| } |
| |
| void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, Node *node) |
| { |
| marker_ = CodeMarker::markerForFileName(node->location().filePath()); |
| QDocIndexFiles::qdocIndexFiles()->generateIndexSections(writer, node, this); |
| // generateIndexSections does nothing for groups, so handle them explicitly |
| if (node->isGroup()) |
| QDocIndexFiles::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().getBool(CONFIG_LOCATIONINFO)) { |
| 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"); |
| CollectionNode *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->getMemberNamespaces(namespaces); |
| 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->getMemberClasses(classes); |
| generateAnnotatedList(writer, node, classes); |
| writer.writeEndElement(); // section |
| } |
| writer.writeEndElement(); // generatedlist |
| } |
| |
| inLink = inContents = inSectionHeading = hasQuotingInformation = false; |
| numTableRows = 0; |
| |
| const Atom *atom = node->doc().body().firstAtom(); |
| while (atom) |
| atom = addAtomElements(writer, atom, node, marker_); |
| |
| QVector<Text> alsoList = node->doc().alsoList(); |
| supplementAlsoList(node, alsoList); |
| |
| if (!alsoList.isEmpty()) { |
| writer.writeStartElement("see-also"); |
| for (int i = 0; i < alsoList.size(); ++i) { |
| const Atom *atom = alsoList.at(i).firstAtom(); |
| while (atom) |
| atom = addAtomElements(writer, atom, node, marker_); |
| } |
| writer.writeEndElement(); // see-also |
| } |
| |
| if (node->isExample()) { |
| supplement = true; |
| generateRequiredLinks(node, marker_); |
| supplement = false; |
| } else if (node->isGroup()) { |
| CollectionNode *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() && !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. |
| 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()) { |
| Aggregate *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 = qdb_->getCollectionNode(atom->string(), Node::Group); |
| if (cn) |
| generateAnnotatedList(writer, relative, cn->members()); |
| } break; |
| case Atom::AutoLink: |
| if (!inLink && !inSectionHeading) { |
| const Node *node = nullptr; |
| QString link = getLink(atom, relative, &node); |
| if (node) { |
| startLink(writer, atom, node, link); |
| if (inLink) { |
| writer.writeCharacters(atom->string()); |
| writer.writeEndElement(); // link |
| inLink = false; |
| } |
| } else { |
| writer.writeCharacters(atom->string()); |
| } |
| } else { |
| writer.writeCharacters(atom->string()); |
| } |
| 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 QVector<QStringRef> words = str.splitRef(' '); |
| if (!words.isEmpty()) { |
| const QStringRef &first(words.at(0)); |
| if (!(first == "contains" || first == "specifies" || first == "describes" |
| || first == "defines" || first == "holds" || first == "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 (inLink) |
| writer.writeAttribute("type", "normal"); |
| else |
| writer.writeAttribute("type", "highlighted"); |
| |
| writer.writeCharacters(plainCode(atom->string())); |
| writer.writeEndElement(); // teletype |
| break; |
| |
| case Atom::Code: |
| if (!hasQuotingInformation) |
| writer.writeTextElement( |
| "code", trimmedTrailing(plainCode(atom->string()), QString(), QString())); |
| else |
| keepQuoting = true; |
| break; |
| |
| #ifdef QDOC_QML |
| case Atom::Qml: |
| if (!hasQuotingInformation) |
| writer.writeTextElement( |
| "qml", trimmedTrailing(plainCode(atom->string()), QString(), QString())); |
| else |
| keepQuoting = true; |
| #endif |
| case Atom::CodeBad: |
| writer.writeTextElement("badcode", |
| trimmedTrailing(plainCode(atom->string()), QString(), QString())); |
| break; |
| |
| case Atom::CodeNew: |
| writer.writeTextElement("para", "you can rewrite it as"); |
| writer.writeTextElement("newcode", |
| trimmedTrailing(plainCode(atom->string()), QString(), QString())); |
| break; |
| |
| case Atom::CodeOld: |
| writer.writeTextElement("para", "For example, if you have code like"); |
| writer.writeTextElement("oldcode", |
| trimmedTrailing(plainCode(atom->string()), QString(), QString())); |
| break; |
| |
| case Atom::CodeQuoteArgument: |
| if (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 (quoting_) { |
| quoteCommand = atom->string(); |
| writer.writeStartElement(quoteCommand); |
| } |
| break; |
| |
| case Atom::ExampleFileLink: { |
| if (!inLink) { |
| QString link = linkForExampleFile(atom->string(), relative); |
| if (!link.isEmpty()) |
| startLink(writer, atom, relative, link); |
| } |
| } break; |
| |
| case Atom::ExampleImageLink: { |
| if (!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(); |
| } |
| if (inLink) { |
| writer.writeEndElement(); // link |
| inLink = false; |
| } |
| break; |
| |
| case Atom::GeneratedList: |
| writer.writeStartElement("generatedlist"); |
| writer.writeAttribute("contents", atom->string()); |
| writer.writeEndElement(); |
| break; |
| case Atom::Image: |
| writer.writeStartElement("image"); |
| writer.writeAttribute("href", imageFileName(relative, atom->string())); |
| writer.writeEndElement(); |
| break; |
| |
| case Atom::InlineImage: |
| writer.writeStartElement("inlineimage"); |
| writer.writeAttribute("href", imageFileName(relative, atom->string())); |
| writer.writeEndElement(); |
| break; |
| |
| case Atom::ImageText: |
| break; |
| |
| case Atom::ImportantLeft: |
| writer.writeStartElement("para"); |
| writer.writeTextElement("bold", "Important:"); |
| writer.writeCharacters(" "); |
| break; |
| |
| case Atom::ImportantRight: |
| writer.writeEndElement(); // para |
| break; |
| |
| case Atom::LegaleseLeft: |
| writer.writeStartElement("legalese"); |
| break; |
| |
| case Atom::LegaleseRight: |
| writer.writeEndElement(); // legalese |
| break; |
| |
| case Atom::Link: |
| case Atom::LinkNode: |
| if (!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; |
| |
| case Atom::NoteRight: |
| writer.writeEndElement(); // para |
| break; |
| |
| case Atom::Nop: |
| break; |
| |
| case Atom::ParaLeft: |
| writer.writeStartElement("para"); |
| break; |
| |
| 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", Doc::canonicalTitle(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)); |
| inSectionHeading = true; |
| } break; |
| |
| case Atom::SectionHeadingRight: |
| writer.writeEndElement(); // heading |
| inSectionHeading = false; |
| break; |
| |
| case Atom::SidebarLeft: |
| case Atom::SidebarRight: |
| break; |
| |
| case Atom::SnippetCommand: |
| if (quoting_) { |
| writer.writeStartElement(atom->string()); |
| } |
| break; |
| |
| case Atom::SnippetIdentifier: |
| if (quoting_) { |
| writer.writeAttribute("identifier", atom->string()); |
| writer.writeEndElement(); |
| keepQuoting = true; |
| } |
| break; |
| |
| case Atom::SnippetLocation: |
| if (quoting_) { |
| const QString location = atom->string(); |
| writer.writeAttribute("location", location); |
| const QString resolved = Doc::resolveFile(Location(), location); |
| if (!resolved.isEmpty()) |
| writer.writeAttribute("path", resolved); |
| } |
| 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", Doc::canonicalTitle(atom->string())); |
| writer.writeEndElement(); |
| break; |
| |
| case Atom::UnhandledFormat: |
| case Atom::UnknownCommand: |
| writer.writeCharacters(atom->typeString()); |
| break; |
| default: |
| break; |
| } |
| |
| 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->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 ExampleNode *en = static_cast<const ExampleNode *>(node); |
| QString fileTitle = exampleFileTitle(en, atom->string()); |
| if (!fileTitle.isEmpty()) { |
| writer.writeAttribute("page", fileTitle); |
| break; |
| } |
| } |
| Q_FALLTHROUGH(); |
| case Node::Page: |
| writer.writeAttribute("page", fullName); |
| break; |
| case Node::Property: { |
| const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node); |
| if (propertyNode->getters().size() > 0) |
| writer.writeAttribute("getter", propertyNode->getters().at(0)->fullName()); |
| } break; |
| default: |
| break; |
| } |
| } |
| inLink = true; |
| } |
| } |
| |
| void WebXMLGenerator::endLink(QXmlStreamWriter &writer) |
| { |
| if (inLink) { |
| writer.writeEndElement(); // link |
| inLink = false; |
| } |
| } |
| |
| void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node) |
| { |
| if (node && !node->links().empty()) { |
| QPair<QString, QString> anchorPair; |
| const Node *linkNode; |
| |
| for (auto it = node->links().cbegin(); it != node->links().cend(); ++it) { |
| |
| linkNode = 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 |
| } |
| |
| QT_END_NAMESPACE |