--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/qdoc3/webxmlgenerator.cpp Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ webxmlgenerator.cpp
+*/
+
+#include <QtXml>
+
+#include "codemarker.h"
+#include "pagegenerator.h"
+#include "webxmlgenerator.h"
+#include "node.h"
+#include "separator.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+
+WebXMLGenerator::WebXMLGenerator()
+ : PageGenerator()
+{
+}
+
+WebXMLGenerator::~WebXMLGenerator()
+{
+}
+
+void WebXMLGenerator::initializeGenerator(const Config &config)
+{
+ Generator::initializeGenerator(config);
+
+ project = config.getString(CONFIG_PROJECT);
+
+ projectDescription = config.getString(CONFIG_DESCRIPTION);
+ if (projectDescription.isEmpty() && !project.isEmpty())
+ projectDescription = project + " Reference Documentation";
+
+ projectUrl = config.getString(CONFIG_URL);
+
+ generateIndex = config.getBool(CONFIG_GENERATEINDEX);
+}
+
+void WebXMLGenerator::terminateGenerator()
+{
+ PageGenerator::terminateGenerator();
+}
+
+QString WebXMLGenerator::format()
+{
+ return "WebXML";
+}
+
+QString WebXMLGenerator::fileExtension(const Node * /* node */)
+{
+ return "xml";
+}
+
+void WebXMLGenerator::generateTree(const Tree *tree, CodeMarker *marker)
+{
+ tre = tree;
+ moduleClassMap.clear();
+ moduleNamespaceMap.clear();
+ serviceClasses.clear();
+ findAllClasses(tree->root());
+ findAllNamespaces(tree->root());
+
+ PageGenerator::generateTree(tree, marker);
+
+ if (generateIndex)
+ tre->generateIndex(outputDir() + "/" + project.toLower() + ".index",
+ projectUrl, projectDescription, false);
+}
+
+void WebXMLGenerator::startText(const Node *relative, CodeMarker *marker)
+{
+ inLink = false;
+ inContents = false;
+ inSectionHeading = false;
+ numTableRows = 0;
+ sectionNumber.clear();
+ PageGenerator::startText(relative, marker);
+}
+
+int WebXMLGenerator::generateAtom(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *relative, CodeMarker *marker)
+{
+ Q_UNUSED(writer);
+
+ int skipAhead = 0;
+
+ switch (atom->type()) {
+ default:
+ PageGenerator::generateAtom(atom, relative, marker);
+ }
+ return skipAhead;
+}
+
+void WebXMLGenerator::generateClassLikeNode(const InnerNode *inner,
+ CodeMarker *marker)
+{
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+
+ generateIndexSections(writer, inner, marker);
+
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ out().flush();
+}
+
+void WebXMLGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
+{
+ QByteArray data;
+ QXmlStreamWriter writer(&data);
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+ writer.writeStartElement("WebXML");
+ writer.writeStartElement("document");
+
+ generateIndexSections(writer, fake, marker);
+
+ writer.writeEndElement(); // document
+ writer.writeEndElement(); // WebXML
+ writer.writeEndDocument();
+
+ out() << data;
+ out().flush();
+}
+
+void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer,
+ const Node *node, CodeMarker *marker)
+{
+ if (tre->generateIndexSection(writer, node, true)) {
+
+ // Add documentation to this node if it exists.
+ writer.writeStartElement("description");
+ 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->type() == Node::Fake) {
+
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+
+ generateRelations(writer, node, marker);
+
+ if (fake->subType() == Node::Module) {
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", "classesbymodule");
+
+ if (moduleNamespaceMap.contains(fake->name())) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Namespaces");
+ writer.writeEndElement(); // heading
+ generateAnnotatedList(writer, fake, marker, moduleNamespaceMap[fake->name()]);
+ writer.writeEndElement(); // section
+ }
+ if (moduleClassMap.contains(fake->name())) {
+ writer.writeStartElement("section");
+ writer.writeStartElement("heading");
+ writer.writeAttribute("level", "1");
+ writer.writeCharacters("Classes");
+ writer.writeEndElement(); // heading
+ generateAnnotatedList(writer, fake, marker, moduleClassMap[fake->name()]);
+ writer.writeEndElement(); // section
+ }
+
+ writer.writeEndElement(); // generatedlist
+ }
+ }
+
+ startText(node, marker);
+
+ 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 (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
+ }
+
+ writer.writeEndElement(); // description
+
+ if (node->isInnerNode()) {
+ const InnerNode *inner = static_cast<const InnerNode *>(node);
+
+ // Recurse to generate an element for this child node and all its children.
+ foreach (const Node *child, inner->childNodes())
+ generateIndexSections(writer, child, marker);
+
+ writer.writeStartElement("related");
+ if (inner->relatedNodes().size() > 0) {
+ foreach (const Node *child, inner->relatedNodes())
+ generateIndexSections(writer, child, marker);
+ }
+ writer.writeEndElement(); // related
+ }
+ writer.writeEndElement();
+ }
+}
+
+void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marker)
+{
+ if (!node->url().isNull())
+ return;
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
+ if (fakeNode->subType() == Node::ExternalPage)
+ return;
+ }
+
+ if ( node->parent() != 0 ) {
+ beginSubPage( node->location(), fileName(node) );
+ if ( node->type() == Node::Namespace || node->type() == Node::Class) {
+ generateClassLikeNode(node, marker);
+ } else if ( node->type() == Node::Fake ) {
+ generateFakeNode(static_cast<const FakeNode *>(node), marker);
+ }
+ endSubPage();
+ }
+
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while ( c != node->childNodes().end() ) {
+ if ((*c)->isInnerNode() && (
+ (*c)->access() != Node::Private || (*c)->status() == Node::Internal))
+ generateInnerNode( (const InnerNode *) *c, marker );
+ ++c;
+ }
+}
+
+const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer,
+ const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ switch (atom->type()) {
+ case Atom::AbstractLeft:
+ case Atom::AbstractRight:
+ break;
+ case Atom::AutoLink:
+ if (!inLink && !inSectionHeading) {
+ const Node *node = findNode(atom, relative, marker);
+ if (node) {
+ startLink(writer, atom, node, relative);
+ 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->type()) {
+ case Node::Property:
+ writer.writeCharacters("This property");
+ break;
+ case Node::Variable:
+ writer.writeCharacters("This variable");
+ break;
+ default:
+ break;
+ }
+ if (relative->type() == Node::Property || relative->type() == Node::Variable) {
+ QString str;
+ const Atom *a = atom->next();
+ while (a != 0 && 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.right(1) == ".")
+ str.chop(1);
+
+ QStringList words = str.split(" ");
+ if (!(words.first() == "contains" || words.first() == "specifies"
+ || words.first() == "describes" || words.first() == "defines"
+ || words.first() == "holds" || words.first() == "determines"))
+ writer.writeCharacters(" holds ");
+ else
+ writer.writeCharacters(" ");
+ }
+ break;
+
+ case Atom::BriefRight:
+ if (relative->type() == Node::Property || relative->type() == Node::Variable)
+ 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:
+ writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+#ifdef QDOC_QML
+ case Atom::Qml:
+ writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string())));
+#endif
+
+ case Atom::CodeBad:
+ writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeNew:
+ writer.writeTextElement("para", "you can rewrite it as");
+ writer.writeTextElement("newcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeOld:
+ writer.writeTextElement("para", "For example, if you have code like");
+ writer.writeTextElement("oldcode", trimmedTrailing(plainCode(atom->string())));
+ break;
+
+ case Atom::CodeQuoteArgument:
+ if (quoteCommand == "dots") {
+ writer.writeAttribute("indent", atom->string());
+ writer.writeCharacters("...");
+ } else
+ writer.writeCharacters(atom->string());
+ writer.writeEndElement(); // code
+ break;
+
+ case Atom::CodeQuoteCommand:
+ quoteCommand = atom->string();
+ writer.writeStartElement(quoteCommand);
+ break;
+
+ case Atom::FootnoteLeft:
+ writer.writeStartElement("footnote");
+ break;
+
+ case Atom::FootnoteRight:
+ writer.writeEndElement(); // footnote
+ break;
+/*
+ case Atom::FormatElse:
+ writer.writeStartElement("else");
+ writer.writeEndElement(); // else
+ 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;
+/* out() << formattingLeftMap()[atom->string()];
+ if ( atom->string() == ATOM_FORMATTING_PARAMETER ) {
+ if ( atom->next() != 0 && atom->next()->type() == Atom::String ) {
+ QRegExp subscriptRegExp( "([a-z]+)_([0-9n])" );
+ if ( subscriptRegExp.exactMatch(atom->next()->string()) ) {
+ out() << subscriptRegExp.cap( 1 ) << "<sub>"
+ << subscriptRegExp.cap( 2 ) << "</sub>";
+ skipAhead = 1;
+ }
+ }
+ }*/
+ 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;
+/* if ( atom->string() == ATOM_FORMATTING_LINK ) {
+ if (inLink) {
+ if ( link.isEmpty() ) {
+ if (showBrokenLinks)
+ out() << "</i>";
+ } else {
+ out() << "</a>";
+ }
+ }
+ inLink = false;
+ } else {
+ out() << formattingRightMap()[atom->string()];
+ }*/
+ case Atom::GeneratedList:
+ writer.writeStartElement("generatedlist");
+ writer.writeAttribute("contents", atom->string());
+ writer.writeEndElement(); // generatedlist
+/*
+ if (atom->string() == "annotatedclasses") {
+ generateAnnotatedList(relative, marker, nonCompatClasses);
+ } else if (atom->string() == "classes") {
+ generateCompactList(relative, marker, nonCompatClasses);
+ } else if (atom->string().contains("classesbymodule")) {
+ QString arg = atom->string().trimmed();
+ QString moduleName = atom->string().mid(atom->string().indexOf(
+ "classesbymodule") + 15).trimmed();
+ if (moduleClassMap.contains(moduleName))
+ generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
+ } else if (atom->string().contains("classesbyedition")) {
+ QString arg = atom->string().trimmed();
+ QString editionName = atom->string().mid(atom->string().indexOf(
+ "classesbyedition") + 16).trimmed();
+ if (editionModuleMap.contains(editionName)) {
+ QMap<QString, const Node *> editionClasses;
+ foreach (const QString &moduleName, editionModuleMap[editionName]) {
+ if (moduleClassMap.contains(moduleName))
+ editionClasses.unite(moduleClassMap[moduleName]);
+ }
+ generateAnnotatedList(relative, marker, editionClasses);
+ }
+ } else if (atom->string() == "classhierarchy") {
+ generateClassHierarchy(relative, marker, nonCompatClasses);
+ } else if (atom->string() == "compatclasses") {
+ generateCompactList(relative, marker, compatClasses);
+ } else if (atom->string() == "functionindex") {
+ generateFunctionIndex(relative, marker);
+ } else if (atom->string() == "legalese") {
+ generateLegaleseList(relative, marker);
+ } else if (atom->string() == "mainclasses") {
+ generateCompactList(relative, marker, mainClasses);
+ } else if (atom->string() == "services") {
+ generateCompactList(relative, marker, serviceClasses);
+ } else if (atom->string() == "overviews") {
+ generateOverviewList(relative, marker);
+ } else if (atom->string() == "namespaces") {
+ generateAnnotatedList(relative, marker, namespaceIndex);
+ } else if (atom->string() == "related") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ QMap<QString, const Node *> groupMembersMap;
+ foreach (Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Fake)
+ groupMembersMap[fullName(node, relative, marker)] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+ } else if (atom->string() == "relatedinline") {
+ const FakeNode *fake = static_cast<const FakeNode *>(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ // Reverse the list into the original scan order.
+ // Should be sorted. But on what? It may not be a
+ // regular class or page definition.
+ QList<const Node *> list;
+ foreach (const Node *node, fake->groupMembers())
+ list.prepend(node);
+ foreach (const Node *node, list)
+ generateBody(node, marker );
+ }
+ }
+ break;
+*/
+ break;
+ case Atom::Image:
+ writer.writeStartElement("image");
+ writer.writeAttribute("href", imageFileName(relative, atom->string()));
+ writer.writeEndElement(); // image
+ break;
+
+ case Atom::InlineImage:
+ writer.writeStartElement("inlineimage");
+ writer.writeAttribute("href", imageFileName(relative, atom->string()));
+ writer.writeEndElement(); // inlineimage
+ break;
+
+ case Atom::ImageText:
+ 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 = findNode(atom, relative, marker);
+ if (node)
+ startLink(writer, atom, node, relative);
+ }
+ 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)
+ writer.writeAttribute("type", "enum");
+ 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::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");
+ writer.writeAttribute("level", atom->string()); // + hOffset(relative)
+ inSectionHeading = true;
+ break;
+
+ case Atom::SectionHeadingRight:
+ writer.writeEndElement(); // heading
+ inSectionHeading = false;
+ break;
+
+ case Atom::SidebarLeft:
+ case Atom::SidebarRight:
+ break;
+
+ case Atom::SnippetCommand:
+ writer.writeStartElement(atom->string());
+ break;
+
+ case Atom::SnippetIdentifier:
+ writer.writeAttribute("identifier", atom->string());
+ writer.writeEndElement(); // snippet
+ break;
+
+ case Atom::SnippetLocation:
+ writer.writeAttribute("location", atom->string());
+ 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::TableOfContents:
+ writer.writeStartElement("tableofcontents");
+ writer.writeAttribute("details", atom->string());
+ {
+ int numColumns = 1;
+ const Node *node = relative;
+
+ Doc::SectioningUnit sectioningUnit = Doc::Section4;
+ QStringList params = atom->string().split(",");
+ QString columnText = params.at(0);
+ QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
+ if (pieces.size() >= 2) {
+ columnText = pieces.at(0);
+ pieces.pop_front();
+ QString path = pieces.join(" ").trimmed();
+ node = findNode(path, relative, marker);
+ if (node)
+ writer.writeAttribute("href", fileName(node));
+ }
+
+ if (params.size() == 2) {
+ numColumns = qMax(columnText.toInt(), numColumns);
+ sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
+ writer.writeAttribute("columns", QString::number(numColumns));
+ writer.writeAttribute("unit", QString::number(sectioningUnit));
+ }
+
+ if (node)
+ generateTableOfContents(writer, node, sectioningUnit, numColumns,
+ relative);
+ }
+ writer.writeEndElement(); // tableofcontents
+ break;
+
+ case Atom::Target:
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", Doc::canonicalTitle(atom->string()));
+ writer.writeEndElement(); // target
+ break;
+
+ case Atom::UnhandledFormat:
+ case Atom::UnknownCommand:
+ writer.writeCharacters(atom->typeString());
+ break;
+ default:
+ break;
+ }
+
+ if (atom)
+ return atom->next();
+
+ return 0;
+}
+/*
+ QDomElement atomElement = document.createElement(atom->typeString().toLower());
+ QDomText atomValue = document.createTextNode(atom->string());
+ atomElement.appendChild(atomValue);
+ descriptionElement.appendChild(atomElement);
+*/
+
+/*
+ ### Warning: findNode() is a modified version of HtmlGenerator::getLink().
+*/
+const Node *WebXMLGenerator::findNode(const Atom *atom, const Node *relative, CodeMarker *marker)
+{
+ return findNode(atom->string(), relative, marker);
+}
+
+const Node *WebXMLGenerator::findNode(const QString &title, const Node *relative, CodeMarker *marker)
+{
+ QString link;
+ if (title.contains(":") &&
+ (title.startsWith("file:")
+ || title.startsWith("http:")
+ || title.startsWith("https:")
+ || title.startsWith("ftp:")
+ || title.startsWith("mailto:"))) {
+
+ return 0;
+ } else if (title.count('@') == 1) {
+ return 0;
+ } else {
+ QStringList path;
+ if (title.contains('#')) {
+ path = title.split('#');
+ } else {
+ path.append(title);
+ }
+
+ const Node *node = 0;
+ Atom *targetAtom = 0;
+
+ QString first = path.first().trimmed();
+ if (first.isEmpty()) {
+ node = relative;
+ } else if (first.endsWith(".html")) {
+ node = tre->root()->findNode(first, Node::Fake);
+ } else {
+ node = marker->resolveTarget(first, tre, relative);
+ if (!node)
+ node = tre->findFakeNodeByTitle(first);
+ if (!node)
+ node = tre->findUnambiguousTarget(first, targetAtom);
+ }
+
+ if (node) {
+ if (!node->url().isEmpty())
+ return node;
+ else
+ path.removeFirst();
+ } else {
+ return 0;
+ }
+
+ while (!path.isEmpty()) {
+ targetAtom = tre->findTarget(path.first(), node);
+ if (targetAtom == 0)
+ break;
+ path.removeFirst();
+ }
+/* We would ideally treat targets as nodes to be consistent.
+ if (targetAtom && node && node->isInnerNode()) {
+ Node *parentNode = const_cast<Node *>(node);
+ node = new TargetNode(static_cast<InnerNode*>(parentNode), first);
+ }
+*/
+ return node;
+ }
+ return 0;
+}
+
+void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom,
+ const Node *node, const Node *relative)
+{
+ QString location = tre->fullDocumentLocation(node);
+ if (!location.isEmpty()) {
+ writer.writeStartElement("link");
+ writer.writeAttribute("raw", atom->string());
+ if (atom->string().contains("#") || node == relative) {
+ QString target = atom->string().split("#").last();
+ Atom *targetAtom = tre->findTarget(target, node);
+ if (targetAtom)
+ location += "#" + Doc::canonicalTitle(target);
+ }
+ writer.writeAttribute("href", location);
+ QString type = targetType(node);
+ writer.writeAttribute("type", type);
+ switch (node->type()) {
+ case Node::Enum:
+ writer.writeAttribute("enum", tre->fullDocumentName(node));
+ break;
+ case Node::Fake:
+ writer.writeAttribute("page", tre->fullDocumentName(node));
+ break;
+ case Node::Property:
+ {
+ const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
+ if (propertyNode->getters().size() > 0)
+ writer.writeAttribute("getter", tre->fullDocumentName(propertyNode->getters()[0]));
+ }
+ default:
+ ;
+ }
+ inLink = true;
+ }
+}
+
+QString WebXMLGenerator::targetType(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ break;
+ case Node::Class:
+ return "class";
+ break;
+ case Node::Fake:
+ return "page";
+ break;
+ case Node::Enum:
+ return "enum";
+ break;
+ case Node::Typedef:
+ return "typedef";
+ break;
+ case Node::Property:
+ return "property";
+ break;
+ case Node::Function:
+ return "function";
+ break;
+ case Node::Variable:
+ return "variable";
+ break;
+ case Node::Target:
+ return "target";
+ break;
+ default:
+ return "";
+ }
+ return "";
+}
+
+void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker)
+{
+ if (node && !node->links().empty()) {
+ QPair<QString,QString> linkPair;
+ QPair<QString,QString> anchorPair;
+ const Node *linkNode;
+
+ foreach (Node::LinkType relation, node->links().keys()) {
+
+ linkPair = node->links()[relation];
+ linkNode = findNode(linkPair.first, node, marker);
+
+ if (!linkNode)
+ linkNode = node;
+
+ if (linkNode == node)
+ anchorPair = linkPair;
+ else
+ anchorPair = anchorForNode(linkNode);
+
+ writer.writeStartElement("relation");
+ writer.writeAttribute("href", anchorPair.first);
+ writer.writeAttribute("type", targetType(linkNode));
+
+ switch (relation) {
+ 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;
+ case Node::IndexLink:
+ writer.writeAttribute("meta", "index");
+ break;
+ default:
+ writer.writeAttribute("meta", "");
+ }
+ writer.writeAttribute("description", anchorPair.second);
+ writer.writeEndElement(); // link
+ }
+ }
+}
+
+// Classes adapted from HtmlGenerator.
+
+void WebXMLGenerator::generateTableOfContents(QXmlStreamWriter &writer, const Node *node,
+ Doc::SectioningUnit sectioningUnit,
+ int numColumns, const Node *relative)
+
+{
+ if (!node->doc().hasTableOfContents())
+ return;
+ QList<Atom *> toc = node->doc().tableOfContents();
+ if (toc.isEmpty())
+ return;
+
+ QString nodeName = "";
+ if (node != relative)
+ nodeName = node->name();
+
+ QStringList sectionNumber;
+ int columnSize = 0;
+
+ if (numColumns > 1) {
+ writer.writeStartElement("table");
+ writer.writeAttribute("width", "100%");
+ writer.writeStartElement("row");
+ writer.writeStartElement("item");
+ writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
+ }
+
+ // disable nested links in table of contents
+ inContents = true;
+ inLink = true;
+
+ for (int i = 0; i < toc.size(); ++i) {
+ Atom *atom = toc.at(i);
+
+ int nextLevel = atom->string().toInt();
+ if (nextLevel > (int)sectioningUnit)
+ continue;
+
+ if (sectionNumber.size() < nextLevel) {
+ do {
+ writer.writeStartElement("list");
+ sectionNumber.append("1");
+ } while (sectionNumber.size() < nextLevel);
+ } else {
+ while (sectionNumber.size() > nextLevel) {
+ writer.writeEndElement();
+ sectionNumber.removeLast();
+ }
+ sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
+ }
+ Text headingText = Text::sectionHeading(atom);
+
+ if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
+ writer.writeEndElement(); // list
+ writer.writeEndElement(); // item
+ writer.writeStartElement("item");
+ writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
+ writer.writeStartElement("list");
+ columnSize = 0;
+ }
+
+ writer.writeStartElement("item");
+ writer.writeStartElement("para");
+ writer.writeStartElement("link");
+ writer.writeAttribute("href", nodeName + "#" + Doc::canonicalTitle(headingText.toString()));
+ writer.writeAttribute("type", "page");
+ writer.writeCharacters(headingText.toString());
+ writer.writeEndElement(); // link
+ writer.writeEndElement(); // para
+ writer.writeEndElement(); // item
+
+ ++columnSize;
+ }
+ while (!sectionNumber.isEmpty()) {
+ writer.writeEndElement(); // list
+ sectionNumber.removeLast();
+ }
+
+ if (numColumns > 1) {
+ writer.writeEndElement(); // item
+ writer.writeEndElement(); // row
+ writer.writeEndElement(); // table
+ }
+
+ inContents = false;
+ inLink = false;
+}
+
+void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer,
+ const Node *relative, CodeMarker *marker, const QMap<QString, const Node *> &nodeMap)
+{
+ writer.writeStartElement("table");
+ writer.writeAttribute("width", "100%");
+
+ foreach (QString name, nodeMap.keys()) {
+ const Node *node = nodeMap[name];
+
+ writer.writeStartElement("row");
+ writer.writeStartElement("heading");
+ generateFullName(writer, node, relative, marker);
+ writer.writeEndElement(); // heading
+
+ writer.writeStartElement("item");
+ writer.writeCharacters(node->doc().briefText().toString());
+ writer.writeEndElement(); // item
+ writer.writeEndElement(); // row
+ }
+ writer.writeEndElement(); // table
+}
+
+void WebXMLGenerator::generateFullName(QXmlStreamWriter &writer,
+ const Node *apparentNode, const Node *relative, CodeMarker *marker,
+ const Node *actualNode)
+{
+ if ( actualNode == 0 )
+ actualNode = apparentNode;
+ writer.writeStartElement("link");
+ writer.writeAttribute("href", tre->fullDocumentLocation(actualNode));
+ writer.writeAttribute("type", targetType(actualNode));
+ writer.writeCharacters(fullName(apparentNode, relative, marker));
+ writer.writeEndElement(); // link
+}
+
+// Classes copied (and slightly adapted) from the HtmlGenerator. These need
+// refactoring into a common ancestor class.
+
+void WebXMLGenerator::findAllClasses(const InnerNode *node)
+{
+ NodeList::const_iterator c = node->childNodes().constBegin();
+ while (c != node->childNodes().constEnd()) {
+ if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
+ if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
+ QString className = (*c)->name();
+ if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
+ !(*c)->parent()->name().isEmpty())
+ className = (*c)->parent()->name()+"::"+className;
+
+ QString moduleName = (*c)->moduleName();
+ if (!moduleName.isEmpty())
+ moduleClassMap[moduleName].insert((*c)->name(), *c);
+
+ QString serviceName =
+ (static_cast<const ClassNode *>(*c))->serviceName();
+ if (!serviceName.isEmpty())
+ serviceClasses.insert(serviceName, *c);
+ } else if ((*c)->isInnerNode()) {
+ findAllClasses(static_cast<InnerNode *>(*c));
+ }
+ }
+ ++c;
+ }
+}
+
+void WebXMLGenerator::findAllNamespaces(const InnerNode *node)
+{
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->access() != Node::Private) {
+ if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
+ findAllNamespaces(static_cast<const InnerNode *>(*c));
+ if ((*c)->type() == Node::Namespace) {
+ const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
+ // Ensure that the namespace's name is not empty (the root
+ // namespace has no name).
+ if (!nspace->name().isEmpty()) {
+ namespaceIndex.insert(nspace->name(), *c);
+ QString moduleName = (*c)->moduleName();
+ if (!moduleName.isEmpty())
+ moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
+ }
+ }
+ }
+ }
+ ++c;
+ }
+}
+
+const QPair<QString,QString> WebXMLGenerator::anchorForNode(const Node *node)
+{
+ QPair<QString,QString> anchorPair;
+
+ anchorPair.first = PageGenerator::fileName(node);
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
+ anchorPair.second = fakeNode->title();
+ }
+
+ return anchorPair;
+}
+
+QT_END_NAMESPACE