Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / tools / qdoc3 / webxmlgenerator.cpp
bloba3c92ce5fc16adedc903fea7d2733f66c661c289
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
43 webxmlgenerator.cpp
46 #include <QtXml>
48 #include "codemarker.h"
49 #include "pagegenerator.h"
50 #include "webxmlgenerator.h"
51 #include "node.h"
52 #include "separator.h"
53 #include "tree.h"
55 QT_BEGIN_NAMESPACE
57 #define COMMAND_VERSION Doc::alias("version")
59 WebXMLGenerator::WebXMLGenerator()
60 : PageGenerator()
64 WebXMLGenerator::~WebXMLGenerator()
68 void WebXMLGenerator::initializeGenerator(const Config &config)
70 Generator::initializeGenerator(config);
72 project = config.getString(CONFIG_PROJECT);
74 projectDescription = config.getString(CONFIG_DESCRIPTION);
75 if (projectDescription.isEmpty() && !project.isEmpty())
76 projectDescription = project + " Reference Documentation";
78 projectUrl = config.getString(CONFIG_URL);
80 generateIndex = config.getBool(CONFIG_GENERATEINDEX);
83 void WebXMLGenerator::terminateGenerator()
85 PageGenerator::terminateGenerator();
88 QString WebXMLGenerator::format()
90 return "WebXML";
93 QString WebXMLGenerator::fileExtension(const Node * /* node */)
95 return "xml";
98 void WebXMLGenerator::generateTree(const Tree *tree, CodeMarker *marker)
100 tre = tree;
101 moduleClassMap.clear();
102 moduleNamespaceMap.clear();
103 serviceClasses.clear();
104 findAllClasses(tree->root());
105 findAllNamespaces(tree->root());
107 PageGenerator::generateTree(tree, marker);
109 if (generateIndex)
110 tre->generateIndex(outputDir() + "/" + project.toLower() + ".index",
111 projectUrl, projectDescription, false);
114 void WebXMLGenerator::startText(const Node *relative, CodeMarker *marker)
116 inLink = false;
117 inContents = false;
118 inSectionHeading = false;
119 numTableRows = 0;
120 sectionNumber.clear();
121 PageGenerator::startText(relative, marker);
124 int WebXMLGenerator::generateAtom(QXmlStreamWriter &writer, const Atom *atom,
125 const Node *relative, CodeMarker *marker)
127 Q_UNUSED(writer);
129 int skipAhead = 0;
131 switch (atom->type()) {
132 default:
133 PageGenerator::generateAtom(atom, relative, marker);
135 return skipAhead;
138 void WebXMLGenerator::generateClassLikeNode(const InnerNode *inner,
139 CodeMarker *marker)
141 QByteArray data;
142 QXmlStreamWriter writer(&data);
143 writer.setAutoFormatting(true);
144 writer.writeStartDocument();
145 writer.writeStartElement("WebXML");
146 writer.writeStartElement("document");
148 generateIndexSections(writer, inner, marker);
150 writer.writeEndElement(); // document
151 writer.writeEndElement(); // WebXML
152 writer.writeEndDocument();
154 out() << data;
155 out().flush();
158 void WebXMLGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
160 QByteArray data;
161 QXmlStreamWriter writer(&data);
162 writer.setAutoFormatting(true);
163 writer.writeStartDocument();
164 writer.writeStartElement("WebXML");
165 writer.writeStartElement("document");
167 generateIndexSections(writer, fake, marker);
169 writer.writeEndElement(); // document
170 writer.writeEndElement(); // WebXML
171 writer.writeEndDocument();
173 out() << data;
174 out().flush();
177 void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer,
178 const Node *node, CodeMarker *marker)
180 if (tre->generateIndexSection(writer, node, true)) {
182 // Add documentation to this node if it exists.
183 writer.writeStartElement("description");
184 writer.writeAttribute("path", node->doc().location().filePath());
185 writer.writeAttribute("line", QString::number(node->doc().location().lineNo()));
186 writer.writeAttribute("column", QString::number(node->doc().location().columnNo()));
188 if (node->type() == Node::Fake) {
190 const FakeNode *fake = static_cast<const FakeNode *>(node);
192 generateRelations(writer, node, marker);
194 if (fake->subType() == Node::Module) {
195 writer.writeStartElement("generatedlist");
196 writer.writeAttribute("contents", "classesbymodule");
198 if (moduleNamespaceMap.contains(fake->name())) {
199 writer.writeStartElement("section");
200 writer.writeStartElement("heading");
201 writer.writeAttribute("level", "1");
202 writer.writeCharacters("Namespaces");
203 writer.writeEndElement(); // heading
204 generateAnnotatedList(writer, fake, marker, moduleNamespaceMap[fake->name()]);
205 writer.writeEndElement(); // section
207 if (moduleClassMap.contains(fake->name())) {
208 writer.writeStartElement("section");
209 writer.writeStartElement("heading");
210 writer.writeAttribute("level", "1");
211 writer.writeCharacters("Classes");
212 writer.writeEndElement(); // heading
213 generateAnnotatedList(writer, fake, marker, moduleClassMap[fake->name()]);
214 writer.writeEndElement(); // section
217 writer.writeEndElement(); // generatedlist
221 startText(node, marker);
223 const Atom *atom = node->doc().body().firstAtom();
224 while (atom)
225 atom = addAtomElements(writer, atom, node, marker);
227 QList<Text> alsoList = node->doc().alsoList();
228 supplementAlsoList(node, alsoList);
230 if (!alsoList.isEmpty()) {
231 writer.writeStartElement("see-also");
232 for (int i = 0; i < alsoList.size(); ++i) {
233 const Atom *atom = alsoList.at(i).firstAtom();
234 while (atom)
235 atom = addAtomElements(writer, atom, node, marker);
237 writer.writeEndElement(); // see-also
240 writer.writeEndElement(); // description
242 if (node->isInnerNode()) {
243 const InnerNode *inner = static_cast<const InnerNode *>(node);
245 // Recurse to generate an element for this child node and all its children.
246 foreach (const Node *child, inner->childNodes())
247 generateIndexSections(writer, child, marker);
249 writer.writeStartElement("related");
250 if (inner->relatedNodes().size() > 0) {
251 foreach (const Node *child, inner->relatedNodes())
252 generateIndexSections(writer, child, marker);
254 writer.writeEndElement(); // related
256 writer.writeEndElement();
260 void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marker)
262 if (!node->url().isNull())
263 return;
265 if (node->type() == Node::Fake) {
266 const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
267 if (fakeNode->subType() == Node::ExternalPage)
268 return;
271 if ( node->parent() != 0 ) {
272 beginSubPage( node->location(), fileName(node) );
273 if ( node->type() == Node::Namespace || node->type() == Node::Class) {
274 generateClassLikeNode(node, marker);
275 } else if ( node->type() == Node::Fake ) {
276 generateFakeNode(static_cast<const FakeNode *>(node), marker);
278 endSubPage();
281 NodeList::ConstIterator c = node->childNodes().begin();
282 while ( c != node->childNodes().end() ) {
283 if ((*c)->isInnerNode() && (
284 (*c)->access() != Node::Private || (*c)->status() == Node::Internal))
285 generateInnerNode( (const InnerNode *) *c, marker );
286 ++c;
290 const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer,
291 const Atom *atom, const Node *relative, CodeMarker *marker)
293 switch (atom->type()) {
294 case Atom::AbstractLeft:
295 case Atom::AbstractRight:
296 break;
297 case Atom::AutoLink:
298 if (!inLink && !inSectionHeading) {
299 const Node *node = findNode(atom, relative, marker);
300 if (node) {
301 startLink(writer, atom, node, relative);
302 if (inLink) {
303 writer.writeCharacters(atom->string());
304 writer.writeEndElement(); // link
305 inLink = false;
307 } else
308 writer.writeCharacters(atom->string());
309 } else
310 writer.writeCharacters(atom->string());
311 break;
312 case Atom::BaseName:
313 break;
314 case Atom::BriefLeft:
316 writer.writeStartElement("brief");
317 switch (relative->type()) {
318 case Node::Property:
319 writer.writeCharacters("This property");
320 break;
321 case Node::Variable:
322 writer.writeCharacters("This variable");
323 break;
324 default:
325 break;
327 if (relative->type() == Node::Property || relative->type() == Node::Variable) {
328 QString str;
329 const Atom *a = atom->next();
330 while (a != 0 && a->type() != Atom::BriefRight) {
331 if (a->type() == Atom::String || a->type() == Atom::AutoLink)
332 str += a->string();
333 a = a->next();
335 str[0] = str[0].toLower();
336 if (str.right(1) == ".")
337 str.chop(1);
339 QStringList words = str.split(" ");
340 if (!(words.first() == "contains" || words.first() == "specifies"
341 || words.first() == "describes" || words.first() == "defines"
342 || words.first() == "holds" || words.first() == "determines"))
343 writer.writeCharacters(" holds ");
344 else
345 writer.writeCharacters(" ");
347 break;
349 case Atom::BriefRight:
350 if (relative->type() == Node::Property || relative->type() == Node::Variable)
351 writer.writeCharacters(".");
353 writer.writeEndElement(); // brief
354 break;
356 case Atom::C:
357 writer.writeStartElement("teletype");
358 if (inLink)
359 writer.writeAttribute("type", "normal");
360 else
361 writer.writeAttribute("type", "highlighted");
363 writer.writeCharacters(plainCode(atom->string()));
364 writer.writeEndElement(); // teletype
365 break;
367 case Atom::Code:
368 writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string())));
369 break;
371 #ifdef QDOC_QML
372 case Atom::Qml:
373 writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string())));
374 #endif
376 case Atom::CodeBad:
377 writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string())));
378 break;
380 case Atom::CodeNew:
381 writer.writeTextElement("para", "you can rewrite it as");
382 writer.writeTextElement("newcode", trimmedTrailing(plainCode(atom->string())));
383 break;
385 case Atom::CodeOld:
386 writer.writeTextElement("para", "For example, if you have code like");
387 writer.writeTextElement("oldcode", trimmedTrailing(plainCode(atom->string())));
388 break;
390 case Atom::CodeQuoteArgument:
391 if (quoteCommand == "dots") {
392 writer.writeAttribute("indent", atom->string());
393 writer.writeCharacters("...");
394 } else
395 writer.writeCharacters(atom->string());
396 writer.writeEndElement(); // code
397 break;
399 case Atom::CodeQuoteCommand:
400 quoteCommand = atom->string();
401 writer.writeStartElement(quoteCommand);
402 break;
404 case Atom::FootnoteLeft:
405 writer.writeStartElement("footnote");
406 break;
408 case Atom::FootnoteRight:
409 writer.writeEndElement(); // footnote
410 break;
412 case Atom::FormatElse:
413 writer.writeStartElement("else");
414 writer.writeEndElement(); // else
415 break;
417 case Atom::FormatEndif:
418 writer.writeEndElement(); // raw
419 break;
420 case Atom::FormatIf:
421 writer.writeStartElement("raw");
422 writer.writeAttribute("format", atom->string());
423 break;
424 case Atom::FormattingLeft:
426 if (atom->string() == ATOM_FORMATTING_BOLD)
427 writer.writeStartElement("bold");
428 else if (atom->string() == ATOM_FORMATTING_ITALIC)
429 writer.writeStartElement("italic");
430 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
431 writer.writeStartElement("underline");
432 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
433 writer.writeStartElement("subscript");
434 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
435 writer.writeStartElement("superscript");
436 else if (atom->string() == ATOM_FORMATTING_TELETYPE)
437 writer.writeStartElement("teletype");
438 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
439 writer.writeStartElement("argument");
440 else if (atom->string() == ATOM_FORMATTING_INDEX)
441 writer.writeStartElement("index");
443 break;
444 /* out() << formattingLeftMap()[atom->string()];
445 if ( atom->string() == ATOM_FORMATTING_PARAMETER ) {
446 if ( atom->next() != 0 && atom->next()->type() == Atom::String ) {
447 QRegExp subscriptRegExp( "([a-z]+)_([0-9n])" );
448 if ( subscriptRegExp.exactMatch(atom->next()->string()) ) {
449 out() << subscriptRegExp.cap( 1 ) << "<sub>"
450 << subscriptRegExp.cap( 2 ) << "</sub>";
451 skipAhead = 1;
455 case Atom::FormattingRight:
457 if (atom->string() == ATOM_FORMATTING_BOLD)
458 writer.writeEndElement();
459 else if (atom->string() == ATOM_FORMATTING_ITALIC)
460 writer.writeEndElement();
461 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
462 writer.writeEndElement();
463 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
464 writer.writeEndElement();
465 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
466 writer.writeEndElement();
467 else if (atom->string() == ATOM_FORMATTING_TELETYPE)
468 writer.writeEndElement();
469 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
470 writer.writeEndElement();
471 else if (atom->string() == ATOM_FORMATTING_INDEX)
472 writer.writeEndElement();
474 if (inLink) {
475 writer.writeEndElement(); // link
476 inLink = false;
478 break;
479 /* if ( atom->string() == ATOM_FORMATTING_LINK ) {
480 if (inLink) {
481 if ( link.isEmpty() ) {
482 if (showBrokenLinks)
483 out() << "</i>";
484 } else {
485 out() << "</a>";
488 inLink = false;
489 } else {
490 out() << formattingRightMap()[atom->string()];
492 case Atom::GeneratedList:
493 writer.writeStartElement("generatedlist");
494 writer.writeAttribute("contents", atom->string());
495 writer.writeEndElement(); // generatedlist
497 if (atom->string() == "annotatedclasses") {
498 generateAnnotatedList(relative, marker, nonCompatClasses);
499 } else if (atom->string() == "classes") {
500 generateCompactList(relative, marker, nonCompatClasses);
501 } else if (atom->string().contains("classesbymodule")) {
502 QString arg = atom->string().trimmed();
503 QString moduleName = atom->string().mid(atom->string().indexOf(
504 "classesbymodule") + 15).trimmed();
505 if (moduleClassMap.contains(moduleName))
506 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
507 } else if (atom->string().contains("classesbyedition")) {
508 QString arg = atom->string().trimmed();
509 QString editionName = atom->string().mid(atom->string().indexOf(
510 "classesbyedition") + 16).trimmed();
511 if (editionModuleMap.contains(editionName)) {
512 QMap<QString, const Node *> editionClasses;
513 foreach (const QString &moduleName, editionModuleMap[editionName]) {
514 if (moduleClassMap.contains(moduleName))
515 editionClasses.unite(moduleClassMap[moduleName]);
517 generateAnnotatedList(relative, marker, editionClasses);
519 } else if (atom->string() == "classhierarchy") {
520 generateClassHierarchy(relative, marker, nonCompatClasses);
521 } else if (atom->string() == "compatclasses") {
522 generateCompactList(relative, marker, compatClasses);
523 } else if (atom->string() == "functionindex") {
524 generateFunctionIndex(relative, marker);
525 } else if (atom->string() == "legalese") {
526 generateLegaleseList(relative, marker);
527 } else if (atom->string() == "mainclasses") {
528 generateCompactList(relative, marker, mainClasses);
529 } else if (atom->string() == "services") {
530 generateCompactList(relative, marker, serviceClasses);
531 } else if (atom->string() == "overviews") {
532 generateOverviewList(relative, marker);
533 } else if (atom->string() == "namespaces") {
534 generateAnnotatedList(relative, marker, namespaceIndex);
535 } else if (atom->string() == "related") {
536 const FakeNode *fake = static_cast<const FakeNode *>(relative);
537 if (fake && !fake->groupMembers().isEmpty()) {
538 QMap<QString, const Node *> groupMembersMap;
539 foreach (Node *node, fake->groupMembers()) {
540 if (node->type() == Node::Fake)
541 groupMembersMap[fullName(node, relative, marker)] = node;
543 generateAnnotatedList(fake, marker, groupMembersMap);
545 } else if (atom->string() == "relatedinline") {
546 const FakeNode *fake = static_cast<const FakeNode *>(relative);
547 if (fake && !fake->groupMembers().isEmpty()) {
548 // Reverse the list into the original scan order.
549 // Should be sorted. But on what? It may not be a
550 // regular class or page definition.
551 QList<const Node *> list;
552 foreach (const Node *node, fake->groupMembers())
553 list.prepend(node);
554 foreach (const Node *node, list)
555 generateBody(node, marker );
558 break;
560 break;
561 case Atom::Image:
562 writer.writeStartElement("image");
563 writer.writeAttribute("href", imageFileName(relative, atom->string()));
564 writer.writeEndElement(); // image
565 break;
567 case Atom::InlineImage:
568 writer.writeStartElement("inlineimage");
569 writer.writeAttribute("href", imageFileName(relative, atom->string()));
570 writer.writeEndElement(); // inlineimage
571 break;
573 case Atom::ImageText:
574 break;
576 case Atom::LegaleseLeft:
577 writer.writeStartElement("legalese");
578 break;
580 case Atom::LegaleseRight:
581 writer.writeEndElement(); // legalese
582 break;
584 case Atom::Link:
585 case Atom::LinkNode:
586 if (!inLink) {
587 const Node *node = findNode(atom, relative, marker);
588 if (node)
589 startLink(writer, atom, node, relative);
591 break;
593 case Atom::ListLeft:
594 writer.writeStartElement("list");
596 if (atom->string() == ATOM_LIST_BULLET)
597 writer.writeAttribute("type", "bullet");
598 else if (atom->string() == ATOM_LIST_TAG)
599 writer.writeAttribute("type", "definition");
600 else if (atom->string() == ATOM_LIST_VALUE)
601 writer.writeAttribute("type", "enum");
602 else {
603 writer.writeAttribute("type", "ordered");
604 if (atom->string() == ATOM_LIST_UPPERALPHA)
605 writer.writeAttribute("start", "A");
606 else if (atom->string() == ATOM_LIST_LOWERALPHA)
607 writer.writeAttribute("start", "a");
608 else if (atom->string() == ATOM_LIST_UPPERROMAN)
609 writer.writeAttribute("start", "I");
610 else if (atom->string() == ATOM_LIST_LOWERROMAN)
611 writer.writeAttribute("start", "i");
612 else // (atom->string() == ATOM_LIST_NUMERIC)
613 writer.writeAttribute("start", "1");
615 break;
617 case Atom::ListItemNumber:
618 break;
620 case Atom::ListTagLeft:
622 writer.writeStartElement("definition");
624 writer.writeTextElement("term", plainCode(
625 marker->markedUpEnumValue(atom->next()->string(), relative)));
627 break;
629 case Atom::ListTagRight:
630 writer.writeEndElement(); // definition
631 break;
633 case Atom::ListItemLeft:
634 writer.writeStartElement("item");
635 break;
637 case Atom::ListItemRight:
638 writer.writeEndElement(); // item
639 break;
641 case Atom::ListRight:
642 writer.writeEndElement(); // list
643 break;
645 case Atom::Nop:
646 break;
648 case Atom::ParaLeft:
649 writer.writeStartElement("para");
650 break;
652 case Atom::ParaRight:
653 writer.writeEndElement(); // para
654 break;
656 case Atom::QuotationLeft:
657 writer.writeStartElement("quote");
658 break;
660 case Atom::QuotationRight:
661 writer.writeEndElement(); // quote
662 break;
664 case Atom::RawString:
665 writer.writeCharacters(atom->string());
666 break;
668 case Atom::SectionLeft:
669 writer.writeStartElement("section");
670 writer.writeAttribute("id", Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
671 break;
673 case Atom::SectionRight:
674 writer.writeEndElement(); // section
675 break;
677 case Atom::SectionHeadingLeft:
678 writer.writeStartElement("heading");
679 writer.writeAttribute("level", atom->string()); // + hOffset(relative)
680 inSectionHeading = true;
681 break;
683 case Atom::SectionHeadingRight:
684 writer.writeEndElement(); // heading
685 inSectionHeading = false;
686 break;
688 case Atom::SidebarLeft:
689 case Atom::SidebarRight:
690 break;
692 case Atom::SnippetCommand:
693 writer.writeStartElement(atom->string());
694 break;
696 case Atom::SnippetIdentifier:
697 writer.writeAttribute("identifier", atom->string());
698 writer.writeEndElement(); // snippet
699 break;
701 case Atom::SnippetLocation:
702 writer.writeAttribute("location", atom->string());
703 break;
705 case Atom::String:
706 writer.writeCharacters(atom->string());
707 break;
709 case Atom::TableLeft:
710 writer.writeStartElement("table");
711 if (atom->string().contains("%"))
712 writer.writeAttribute("width", atom->string());
713 break;
715 case Atom::TableRight:
716 writer.writeEndElement(); // table
717 break;
719 case Atom::TableHeaderLeft:
720 writer.writeStartElement("header");
721 break;
723 case Atom::TableHeaderRight:
724 writer.writeEndElement(); // header
725 break;
727 case Atom::TableRowLeft:
728 writer.writeStartElement("row");
729 break;
731 case Atom::TableRowRight:
732 writer.writeEndElement(); // row
733 break;
735 case Atom::TableItemLeft:
737 writer.writeStartElement("item");
738 QStringList spans = atom->string().split(",");
739 if (spans.size() == 2) {
740 if (spans.at(0) != "1")
741 writer.writeAttribute("colspan", spans.at(0).trimmed());
742 if (spans.at(1) != "1")
743 writer.writeAttribute("rowspan", spans.at(1).trimmed());
746 break;
748 case Atom::TableItemRight:
749 writer.writeEndElement(); // item
750 break;
752 case Atom::TableOfContents:
753 writer.writeStartElement("tableofcontents");
754 writer.writeAttribute("details", atom->string());
756 int numColumns = 1;
757 const Node *node = relative;
759 Doc::SectioningUnit sectioningUnit = Doc::Section4;
760 QStringList params = atom->string().split(",");
761 QString columnText = params.at(0);
762 QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
763 if (pieces.size() >= 2) {
764 columnText = pieces.at(0);
765 pieces.pop_front();
766 QString path = pieces.join(" ").trimmed();
767 node = findNode(path, relative, marker);
768 if (node)
769 writer.writeAttribute("href", fileName(node));
772 if (params.size() == 2) {
773 numColumns = qMax(columnText.toInt(), numColumns);
774 sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt();
775 writer.writeAttribute("columns", QString::number(numColumns));
776 writer.writeAttribute("unit", QString::number(sectioningUnit));
779 if (node)
780 generateTableOfContents(writer, node, sectioningUnit, numColumns,
781 relative);
783 writer.writeEndElement(); // tableofcontents
784 break;
786 case Atom::Target:
787 writer.writeStartElement("target");
788 writer.writeAttribute("name", Doc::canonicalTitle(atom->string()));
789 writer.writeEndElement(); // target
790 break;
792 case Atom::UnhandledFormat:
793 case Atom::UnknownCommand:
794 writer.writeCharacters(atom->typeString());
795 break;
796 default:
797 break;
800 if (atom)
801 return atom->next();
803 return 0;
806 QDomElement atomElement = document.createElement(atom->typeString().toLower());
807 QDomText atomValue = document.createTextNode(atom->string());
808 atomElement.appendChild(atomValue);
809 descriptionElement.appendChild(atomElement);
813 ### Warning: findNode() is a modified version of HtmlGenerator::getLink().
815 const Node *WebXMLGenerator::findNode(const Atom *atom, const Node *relative, CodeMarker *marker)
817 return findNode(atom->string(), relative, marker);
820 const Node *WebXMLGenerator::findNode(const QString &title, const Node *relative, CodeMarker *marker)
822 QString link;
823 if (title.contains(":") &&
824 (title.startsWith("file:")
825 || title.startsWith("http:")
826 || title.startsWith("https:")
827 || title.startsWith("ftp:")
828 || title.startsWith("mailto:"))) {
830 return 0;
831 } else if (title.count('@') == 1) {
832 return 0;
833 } else {
834 QStringList path;
835 if (title.contains('#')) {
836 path = title.split('#');
837 } else {
838 path.append(title);
841 const Node *node = 0;
842 Atom *targetAtom = 0;
844 QString first = path.first().trimmed();
845 if (first.isEmpty()) {
846 node = relative;
847 } else if (first.endsWith(".html")) {
848 node = tre->root()->findNode(first, Node::Fake);
849 } else {
850 node = marker->resolveTarget(first, tre, relative);
851 if (!node)
852 node = tre->findFakeNodeByTitle(first);
853 if (!node)
854 node = tre->findUnambiguousTarget(first, targetAtom);
857 if (node) {
858 if (!node->url().isEmpty())
859 return node;
860 else
861 path.removeFirst();
862 } else {
863 return 0;
866 while (!path.isEmpty()) {
867 targetAtom = tre->findTarget(path.first(), node);
868 if (targetAtom == 0)
869 break;
870 path.removeFirst();
872 /* We would ideally treat targets as nodes to be consistent.
873 if (targetAtom && node && node->isInnerNode()) {
874 Node *parentNode = const_cast<Node *>(node);
875 node = new TargetNode(static_cast<InnerNode*>(parentNode), first);
878 return node;
880 return 0;
883 void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom,
884 const Node *node, const Node *relative)
886 QString location = tre->fullDocumentLocation(node);
887 if (!location.isEmpty()) {
888 writer.writeStartElement("link");
889 writer.writeAttribute("raw", atom->string());
890 if (atom->string().contains("#") || node == relative) {
891 QString target = atom->string().split("#").last();
892 Atom *targetAtom = tre->findTarget(target, node);
893 if (targetAtom)
894 location += "#" + Doc::canonicalTitle(target);
896 writer.writeAttribute("href", location);
897 QString type = targetType(node);
898 writer.writeAttribute("type", type);
899 switch (node->type()) {
900 case Node::Enum:
901 writer.writeAttribute("enum", tre->fullDocumentName(node));
902 break;
903 case Node::Fake:
904 writer.writeAttribute("page", tre->fullDocumentName(node));
905 break;
906 case Node::Property:
908 const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
909 if (propertyNode->getters().size() > 0)
910 writer.writeAttribute("getter", tre->fullDocumentName(propertyNode->getters()[0]));
912 default:
915 inLink = true;
919 QString WebXMLGenerator::targetType(const Node *node)
921 switch (node->type()) {
922 case Node::Namespace:
923 return "namespace";
924 break;
925 case Node::Class:
926 return "class";
927 break;
928 case Node::Fake:
929 return "page";
930 break;
931 case Node::Enum:
932 return "enum";
933 break;
934 case Node::Typedef:
935 return "typedef";
936 break;
937 case Node::Property:
938 return "property";
939 break;
940 case Node::Function:
941 return "function";
942 break;
943 case Node::Variable:
944 return "variable";
945 break;
946 case Node::Target:
947 return "target";
948 break;
949 default:
950 return "";
952 return "";
955 void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker)
957 if (node && !node->links().empty()) {
958 QPair<QString,QString> linkPair;
959 QPair<QString,QString> anchorPair;
960 const Node *linkNode;
962 foreach (Node::LinkType relation, node->links().keys()) {
964 linkPair = node->links()[relation];
965 linkNode = findNode(linkPair.first, node, marker);
967 if (!linkNode)
968 linkNode = node;
970 if (linkNode == node)
971 anchorPair = linkPair;
972 else
973 anchorPair = anchorForNode(linkNode);
975 writer.writeStartElement("relation");
976 writer.writeAttribute("href", anchorPair.first);
977 writer.writeAttribute("type", targetType(linkNode));
979 switch (relation) {
980 case Node::StartLink:
981 writer.writeAttribute("meta", "start");
982 break;
983 case Node::NextLink:
984 writer.writeAttribute("meta", "next");
985 break;
986 case Node::PreviousLink:
987 writer.writeAttribute("meta", "previous");
988 break;
989 case Node::ContentsLink:
990 writer.writeAttribute("meta", "contents");
991 break;
992 case Node::IndexLink:
993 writer.writeAttribute("meta", "index");
994 break;
995 default:
996 writer.writeAttribute("meta", "");
998 writer.writeAttribute("description", anchorPair.second);
999 writer.writeEndElement(); // link
1004 // Classes adapted from HtmlGenerator.
1006 void WebXMLGenerator::generateTableOfContents(QXmlStreamWriter &writer, const Node *node,
1007 Doc::SectioningUnit sectioningUnit,
1008 int numColumns, const Node *relative)
1011 if (!node->doc().hasTableOfContents())
1012 return;
1013 QList<Atom *> toc = node->doc().tableOfContents();
1014 if (toc.isEmpty())
1015 return;
1017 QString nodeName = "";
1018 if (node != relative)
1019 nodeName = node->name();
1021 QStringList sectionNumber;
1022 int columnSize = 0;
1024 if (numColumns > 1) {
1025 writer.writeStartElement("table");
1026 writer.writeAttribute("width", "100%");
1027 writer.writeStartElement("row");
1028 writer.writeStartElement("item");
1029 writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
1032 // disable nested links in table of contents
1033 inContents = true;
1034 inLink = true;
1036 for (int i = 0; i < toc.size(); ++i) {
1037 Atom *atom = toc.at(i);
1039 int nextLevel = atom->string().toInt();
1040 if (nextLevel > (int)sectioningUnit)
1041 continue;
1043 if (sectionNumber.size() < nextLevel) {
1044 do {
1045 writer.writeStartElement("list");
1046 sectionNumber.append("1");
1047 } while (sectionNumber.size() < nextLevel);
1048 } else {
1049 while (sectionNumber.size() > nextLevel) {
1050 writer.writeEndElement();
1051 sectionNumber.removeLast();
1053 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
1055 Text headingText = Text::sectionHeading(atom);
1057 if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
1058 writer.writeEndElement(); // list
1059 writer.writeEndElement(); // item
1060 writer.writeStartElement("item");
1061 writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%");
1062 writer.writeStartElement("list");
1063 columnSize = 0;
1066 writer.writeStartElement("item");
1067 writer.writeStartElement("para");
1068 writer.writeStartElement("link");
1069 writer.writeAttribute("href", nodeName + "#" + Doc::canonicalTitle(headingText.toString()));
1070 writer.writeAttribute("type", "page");
1071 writer.writeCharacters(headingText.toString());
1072 writer.writeEndElement(); // link
1073 writer.writeEndElement(); // para
1074 writer.writeEndElement(); // item
1076 ++columnSize;
1078 while (!sectionNumber.isEmpty()) {
1079 writer.writeEndElement(); // list
1080 sectionNumber.removeLast();
1083 if (numColumns > 1) {
1084 writer.writeEndElement(); // item
1085 writer.writeEndElement(); // row
1086 writer.writeEndElement(); // table
1089 inContents = false;
1090 inLink = false;
1093 void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer,
1094 const Node *relative, CodeMarker *marker, const QMap<QString, const Node *> &nodeMap)
1096 writer.writeStartElement("table");
1097 writer.writeAttribute("width", "100%");
1099 foreach (QString name, nodeMap.keys()) {
1100 const Node *node = nodeMap[name];
1102 writer.writeStartElement("row");
1103 writer.writeStartElement("heading");
1104 generateFullName(writer, node, relative, marker);
1105 writer.writeEndElement(); // heading
1107 writer.writeStartElement("item");
1108 writer.writeCharacters(node->doc().briefText().toString());
1109 writer.writeEndElement(); // item
1110 writer.writeEndElement(); // row
1112 writer.writeEndElement(); // table
1115 void WebXMLGenerator::generateFullName(QXmlStreamWriter &writer,
1116 const Node *apparentNode, const Node *relative, CodeMarker *marker,
1117 const Node *actualNode)
1119 if ( actualNode == 0 )
1120 actualNode = apparentNode;
1121 writer.writeStartElement("link");
1122 writer.writeAttribute("href", tre->fullDocumentLocation(actualNode));
1123 writer.writeAttribute("type", targetType(actualNode));
1124 writer.writeCharacters(fullName(apparentNode, relative, marker));
1125 writer.writeEndElement(); // link
1128 // Classes copied (and slightly adapted) from the HtmlGenerator. These need
1129 // refactoring into a common ancestor class.
1131 void WebXMLGenerator::findAllClasses(const InnerNode *node)
1133 NodeList::const_iterator c = node->childNodes().constBegin();
1134 while (c != node->childNodes().constEnd()) {
1135 if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
1136 if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
1137 QString className = (*c)->name();
1138 if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace &&
1139 !(*c)->parent()->name().isEmpty())
1140 className = (*c)->parent()->name()+"::"+className;
1142 QString moduleName = (*c)->moduleName();
1143 if (!moduleName.isEmpty())
1144 moduleClassMap[moduleName].insert((*c)->name(), *c);
1146 QString serviceName =
1147 (static_cast<const ClassNode *>(*c))->serviceName();
1148 if (!serviceName.isEmpty())
1149 serviceClasses.insert(serviceName, *c);
1150 } else if ((*c)->isInnerNode()) {
1151 findAllClasses(static_cast<InnerNode *>(*c));
1154 ++c;
1158 void WebXMLGenerator::findAllNamespaces(const InnerNode *node)
1160 NodeList::ConstIterator c = node->childNodes().begin();
1161 while (c != node->childNodes().end()) {
1162 if ((*c)->access() != Node::Private) {
1163 if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
1164 findAllNamespaces(static_cast<const InnerNode *>(*c));
1165 if ((*c)->type() == Node::Namespace) {
1166 const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
1167 // Ensure that the namespace's name is not empty (the root
1168 // namespace has no name).
1169 if (!nspace->name().isEmpty()) {
1170 namespaceIndex.insert(nspace->name(), *c);
1171 QString moduleName = (*c)->moduleName();
1172 if (!moduleName.isEmpty())
1173 moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
1178 ++c;
1182 const QPair<QString,QString> WebXMLGenerator::anchorForNode(const Node *node)
1184 QPair<QString,QString> anchorPair;
1186 anchorPair.first = PageGenerator::fileName(node);
1187 if (node->type() == Node::Fake) {
1188 const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
1189 anchorPair.second = fakeNode->title();
1192 return anchorPair;
1195 QT_END_NAMESPACE