source: trunk/tools/qdoc3/webxmlgenerator.cpp@ 1077

Last change on this file since 1077 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 39.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 webxmlgenerator.cpp
44*/
45
46#include "codemarker.h"
47#include "pagegenerator.h"
48#include "webxmlgenerator.h"
49#include "node.h"
50#include "separator.h"
51#include "tree.h"
52
53#include <QtCore/qxmlstream.h>
54
55QT_BEGIN_NAMESPACE
56
57#define COMMAND_VERSION Doc::alias("version")
58
59WebXMLGenerator::WebXMLGenerator()
60 : PageGenerator()
61{
62}
63
64WebXMLGenerator::~WebXMLGenerator()
65{
66}
67
68void WebXMLGenerator::initializeGenerator(const Config &config)
69{
70 Generator::initializeGenerator(config);
71
72 project = config.getString(CONFIG_PROJECT);
73
74 projectDescription = config.getString(CONFIG_DESCRIPTION);
75 if (projectDescription.isEmpty() && !project.isEmpty())
76 projectDescription = project + " Reference Documentation";
77
78 projectUrl = config.getString(CONFIG_URL);
79
80 generateIndex = config.getBool(CONFIG_GENERATEINDEX);
81}
82
83void WebXMLGenerator::terminateGenerator()
84{
85 PageGenerator::terminateGenerator();
86}
87
88QString WebXMLGenerator::format()
89{
90 return "WebXML";
91}
92
93QString WebXMLGenerator::fileExtension(const Node * /* node */) const
94{
95 return "xml";
96}
97
98void WebXMLGenerator::generateTree(const Tree *tree, CodeMarker *marker)
99{
100 tre = tree;
101 moduleClassMap.clear();
102 moduleNamespaceMap.clear();
103 serviceClasses.clear();
104 findAllClasses(tree->root());
105 findAllNamespaces(tree->root());
106
107 PageGenerator::generateTree(tree, marker);
108
109 if (generateIndex)
110 tre->generateIndex(outputDir() + "/" + project.toLower() + ".index",
111 projectUrl, projectDescription, false);
112}
113
114void WebXMLGenerator::startText(const Node *relative, CodeMarker *marker)
115{
116 inLink = false;
117 inContents = false;
118 inSectionHeading = false;
119 numTableRows = 0;
120 sectionNumber.clear();
121 PageGenerator::startText(relative, marker);
122}
123
124int WebXMLGenerator::generateAtom(QXmlStreamWriter &writer, const Atom *atom,
125 const Node *relative, CodeMarker *marker)
126{
127 Q_UNUSED(writer);
128
129 int skipAhead = 0;
130
131 switch (atom->type()) {
132 default:
133 PageGenerator::generateAtom(atom, relative, marker);
134 }
135 return skipAhead;
136}
137
138void WebXMLGenerator::generateClassLikeNode(const InnerNode *inner,
139 CodeMarker *marker)
140{
141 QByteArray data;
142 QXmlStreamWriter writer(&data);
143 writer.setAutoFormatting(true);
144 writer.writeStartDocument();
145 writer.writeStartElement("WebXML");
146 writer.writeStartElement("document");
147
148 generateIndexSections(writer, inner, marker);
149
150 writer.writeEndElement(); // document
151 writer.writeEndElement(); // WebXML
152 writer.writeEndDocument();
153
154 out() << data;
155 out().flush();
156}
157
158void WebXMLGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
159{
160 QByteArray data;
161 QXmlStreamWriter writer(&data);
162 writer.setAutoFormatting(true);
163 writer.writeStartDocument();
164 writer.writeStartElement("WebXML");
165 writer.writeStartElement("document");
166
167 generateIndexSections(writer, fake, marker);
168
169 writer.writeEndElement(); // document
170 writer.writeEndElement(); // WebXML
171 writer.writeEndDocument();
172
173 out() << data;
174 out().flush();
175}
176
177void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer,
178 const Node *node, CodeMarker *marker)
179{
180 if (tre->generateIndexSection(writer, node, true)) {
181
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()));
187
188 if (node->type() == Node::Fake) {
189
190 const FakeNode *fake = static_cast<const FakeNode *>(node);
191
192 generateRelations(writer, node, marker);
193
194 if (fake->subType() == Node::Module) {
195 writer.writeStartElement("generatedlist");
196 writer.writeAttribute("contents", "classesbymodule");
197
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
206 }
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
215 }
216
217 writer.writeEndElement(); // generatedlist
218 }
219 }
220
221 startText(node, marker);
222
223 const Atom *atom = node->doc().body().firstAtom();
224 while (atom)
225 atom = addAtomElements(writer, atom, node, marker);
226
227 QList<Text> alsoList = node->doc().alsoList();
228 supplementAlsoList(node, alsoList);
229
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);
236 }
237 writer.writeEndElement(); // see-also
238 }
239
240 writer.writeEndElement(); // description
241
242 if (node->isInnerNode()) {
243 const InnerNode *inner = static_cast<const InnerNode *>(node);
244
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);
248
249 writer.writeStartElement("related");
250 if (inner->relatedNodes().size() > 0) {
251 foreach (const Node *child, inner->relatedNodes())
252 generateIndexSections(writer, child, marker);
253 }
254 writer.writeEndElement(); // related
255 }
256 writer.writeEndElement();
257 }
258}
259
260void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marker)
261{
262 if (!node->url().isNull())
263 return;
264
265 if (node->type() == Node::Fake) {
266 const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
267 if (fakeNode->subType() == Node::ExternalPage)
268 return;
269 }
270
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);
277 }
278 endSubPage();
279 }
280
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;
287 }
288}
289
290const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer,
291 const Atom *atom, const Node *relative, CodeMarker *marker)
292{
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;
306 }
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:
315
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;
326 }
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();
334 }
335 str[0] = str[0].toLower();
336 if (str.right(1) == ".")
337 str.chop(1);
338
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(" ");
346 }
347 break;
348
349 case Atom::BriefRight:
350 if (relative->type() == Node::Property || relative->type() == Node::Variable)
351 writer.writeCharacters(".");
352
353 writer.writeEndElement(); // brief
354 break;
355
356 case Atom::C:
357 writer.writeStartElement("teletype");
358 if (inLink)
359 writer.writeAttribute("type", "normal");
360 else
361 writer.writeAttribute("type", "highlighted");
362
363 writer.writeCharacters(plainCode(atom->string()));
364 writer.writeEndElement(); // teletype
365 break;
366
367 case Atom::Code:
368 writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string())));
369 break;
370
371#ifdef QDOC_QML
372 case Atom::Qml:
373 writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string())));
374#endif
375
376 case Atom::CodeBad:
377 writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string())));
378 break;
379
380 case Atom::CodeNew:
381 writer.writeTextElement("para", "you can rewrite it as");
382 writer.writeTextElement("newcode", trimmedTrailing(plainCode(atom->string())));
383 break;
384
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;
389
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;
398
399 case Atom::CodeQuoteCommand:
400 quoteCommand = atom->string();
401 writer.writeStartElement(quoteCommand);
402 break;
403
404 case Atom::FootnoteLeft:
405 writer.writeStartElement("footnote");
406 break;
407
408 case Atom::FootnoteRight:
409 writer.writeEndElement(); // footnote
410 break;
411/*
412 case Atom::FormatElse:
413 writer.writeStartElement("else");
414 writer.writeEndElement(); // else
415 break;
416*/
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:
425 {
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");
442 }
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;
452 }
453 }
454 }*/
455 case Atom::FormattingRight:
456 {
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();
473 }
474 if (inLink) {
475 writer.writeEndElement(); // link
476 inLink = false;
477 }
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>";
486 }
487 }
488 inLink = false;
489 } else {
490 out() << formattingRightMap()[atom->string()];
491 }*/
492 case Atom::GeneratedList:
493 writer.writeStartElement("generatedlist");
494 writer.writeAttribute("contents", atom->string());
495 writer.writeEndElement(); // generatedlist
496/*
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]);
516 }
517 generateAnnotatedList(relative, marker, editionClasses);
518 }
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;
542 }
543 generateAnnotatedList(fake, marker, groupMembersMap);
544 }
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 );
556 }
557 }
558 break;
559*/
560 break;
561 case Atom::Image:
562 writer.writeStartElement("image");
563 writer.writeAttribute("href", imageFileName(relative, atom->string()));
564 writer.writeEndElement(); // image
565 break;
566
567 case Atom::InlineImage:
568 writer.writeStartElement("inlineimage");
569 writer.writeAttribute("href", imageFileName(relative, atom->string()));
570 writer.writeEndElement(); // inlineimage
571 break;
572
573 case Atom::ImageText:
574 break;
575
576 case Atom::LegaleseLeft:
577 writer.writeStartElement("legalese");
578 break;
579
580 case Atom::LegaleseRight:
581 writer.writeEndElement(); // legalese
582 break;
583
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);
590 }
591 break;
592
593 case Atom::ListLeft:
594 writer.writeStartElement("list");
595
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");
614 }
615 break;
616
617 case Atom::ListItemNumber:
618 break;
619
620 case Atom::ListTagLeft:
621 {
622 writer.writeStartElement("definition");
623
624 writer.writeTextElement("term", plainCode(
625 marker->markedUpEnumValue(atom->next()->string(), relative)));
626 }
627 break;
628
629 case Atom::ListTagRight:
630 writer.writeEndElement(); // definition
631 break;
632
633 case Atom::ListItemLeft:
634 writer.writeStartElement("item");
635 break;
636
637 case Atom::ListItemRight:
638 writer.writeEndElement(); // item
639 break;
640
641 case Atom::ListRight:
642 writer.writeEndElement(); // list
643 break;
644
645 case Atom::Nop:
646 break;
647
648 case Atom::ParaLeft:
649 writer.writeStartElement("para");
650 break;
651
652 case Atom::ParaRight:
653 writer.writeEndElement(); // para
654 break;
655
656 case Atom::QuotationLeft:
657 writer.writeStartElement("quote");
658 break;
659
660 case Atom::QuotationRight:
661 writer.writeEndElement(); // quote
662 break;
663
664 case Atom::RawString:
665 writer.writeCharacters(atom->string());
666 break;
667
668 case Atom::SectionLeft:
669 writer.writeStartElement("section");
670 writer.writeAttribute("id", Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
671 break;
672
673 case Atom::SectionRight:
674 writer.writeEndElement(); // section
675 break;
676
677 case Atom::SectionHeadingLeft:
678 writer.writeStartElement("heading");
679 writer.writeAttribute("level", atom->string()); // + hOffset(relative)
680 inSectionHeading = true;
681 break;
682
683 case Atom::SectionHeadingRight:
684 writer.writeEndElement(); // heading
685 inSectionHeading = false;
686 break;
687
688 case Atom::SidebarLeft:
689 case Atom::SidebarRight:
690 break;
691
692 case Atom::SnippetCommand:
693 writer.writeStartElement(atom->string());
694 break;
695
696 case Atom::SnippetIdentifier:
697 writer.writeAttribute("identifier", atom->string());
698 writer.writeEndElement(); // snippet
699 break;
700
701 case Atom::SnippetLocation:
702 writer.writeAttribute("location", atom->string());
703 break;
704
705 case Atom::String:
706 writer.writeCharacters(atom->string());
707 break;
708
709 case Atom::TableLeft:
710 writer.writeStartElement("table");
711 if (atom->string().contains("%"))
712 writer.writeAttribute("width", atom->string());
713 break;
714
715 case Atom::TableRight:
716 writer.writeEndElement(); // table
717 break;
718
719 case Atom::TableHeaderLeft:
720 writer.writeStartElement("header");
721 break;
722
723 case Atom::TableHeaderRight:
724 writer.writeEndElement(); // header
725 break;
726
727 case Atom::TableRowLeft:
728 writer.writeStartElement("row");
729 break;
730
731 case Atom::TableRowRight:
732 writer.writeEndElement(); // row
733 break;
734
735 case Atom::TableItemLeft:
736 {
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());
744 }
745 }
746 break;
747
748 case Atom::TableItemRight:
749 writer.writeEndElement(); // item
750 break;
751
752 case Atom::TableOfContents:
753 writer.writeStartElement("tableofcontents");
754 writer.writeAttribute("details", atom->string());
755 {
756 int numColumns = 1;
757 const Node *node = relative;
758
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));
770 }
771
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));
777 }
778
779 if (node)
780 generateTableOfContents(writer, node, sectioningUnit, numColumns,
781 relative);
782 }
783 writer.writeEndElement(); // tableofcontents
784 break;
785
786 case Atom::Target:
787 writer.writeStartElement("target");
788 writer.writeAttribute("name", Doc::canonicalTitle(atom->string()));
789 writer.writeEndElement(); // target
790 break;
791
792 case Atom::UnhandledFormat:
793 case Atom::UnknownCommand:
794 writer.writeCharacters(atom->typeString());
795 break;
796 default:
797 break;
798 }
799
800 if (atom)
801 return atom->next();
802
803 return 0;
804}
805/*
806 QDomElement atomElement = document.createElement(atom->typeString().toLower());
807 QDomText atomValue = document.createTextNode(atom->string());
808 atomElement.appendChild(atomValue);
809 descriptionElement.appendChild(atomElement);
810*/
811
812/*
813 ### Warning: findNode() is a modified version of HtmlGenerator::getLink().
814*/
815const Node *WebXMLGenerator::findNode(const Atom *atom, const Node *relative, CodeMarker *marker)
816{
817 return findNode(atom->string(), relative, marker);
818}
819
820const Node *WebXMLGenerator::findNode(const QString &title, const Node *relative, CodeMarker *marker)
821{
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:"))) {
829
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);
839 }
840
841 const Node *node = 0;
842 Atom *targetAtom = 0;
843
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);
855 }
856
857 if (node) {
858 if (!node->url().isEmpty())
859 return node;
860 else
861 path.removeFirst();
862 } else {
863 return 0;
864 }
865
866 while (!path.isEmpty()) {
867 targetAtom = tre->findTarget(path.first(), node);
868 if (targetAtom == 0)
869 break;
870 path.removeFirst();
871 }
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);
876 }
877*/
878 return node;
879 }
880 return 0;
881}
882
883void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom,
884 const Node *node, const Node *relative)
885{
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);
895 }
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:
907 {
908 const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node);
909 if (propertyNode->getters().size() > 0)
910 writer.writeAttribute("getter", tre->fullDocumentName(propertyNode->getters()[0]));
911 }
912 default:
913 ;
914 }
915 inLink = true;
916 }
917}
918
919QString WebXMLGenerator::targetType(const Node *node)
920{
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 "";
951 }
952 return "";
953}
954
955void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker)
956{
957 if (node && !node->links().empty()) {
958 QPair<QString,QString> linkPair;
959 QPair<QString,QString> anchorPair;
960 const Node *linkNode;
961
962 foreach (Node::LinkType relation, node->links().keys()) {
963
964 linkPair = node->links()[relation];
965 linkNode = findNode(linkPair.first, node, marker);
966
967 if (!linkNode)
968 linkNode = node;
969
970 if (linkNode == node)
971 anchorPair = linkPair;
972 else
973 anchorPair = anchorForNode(linkNode);
974
975 writer.writeStartElement("relation");
976 writer.writeAttribute("href", anchorPair.first);
977 writer.writeAttribute("type", targetType(linkNode));
978
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", "");
997 }
998 writer.writeAttribute("description", anchorPair.second);
999 writer.writeEndElement(); // link
1000 }
1001 }
1002}
1003
1004// Classes adapted from HtmlGenerator.
1005
1006void WebXMLGenerator::generateTableOfContents(QXmlStreamWriter &writer, const Node *node,
1007 Doc::SectioningUnit sectioningUnit,
1008 int numColumns, const Node *relative)
1009
1010{
1011 if (!node->doc().hasTableOfContents())
1012 return;
1013 QList<Atom *> toc = node->doc().tableOfContents();
1014 if (toc.isEmpty())
1015 return;
1016
1017 QString nodeName = "";
1018 if (node != relative)
1019 nodeName = node->name();
1020
1021 QStringList sectionNumber;
1022 int columnSize = 0;
1023
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) + "%");
1030 }
1031
1032 // disable nested links in table of contents
1033 inContents = true;
1034 inLink = true;
1035
1036 for (int i = 0; i < toc.size(); ++i) {
1037 Atom *atom = toc.at(i);
1038
1039 int nextLevel = atom->string().toInt();
1040 if (nextLevel > (int)sectioningUnit)
1041 continue;
1042
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();
1052 }
1053 sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
1054 }
1055 Text headingText = Text::sectionHeading(atom);
1056
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;
1064 }
1065
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
1075
1076 ++columnSize;
1077 }
1078 while (!sectionNumber.isEmpty()) {
1079 writer.writeEndElement(); // list
1080 sectionNumber.removeLast();
1081 }
1082
1083 if (numColumns > 1) {
1084 writer.writeEndElement(); // item
1085 writer.writeEndElement(); // row
1086 writer.writeEndElement(); // table
1087 }
1088
1089 inContents = false;
1090 inLink = false;
1091}
1092
1093void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer,
1094 const Node *relative, CodeMarker *marker, const QMap<QString, const Node *> &nodeMap)
1095{
1096 writer.writeStartElement("table");
1097 writer.writeAttribute("width", "100%");
1098
1099 foreach (QString name, nodeMap.keys()) {
1100 const Node *node = nodeMap[name];
1101
1102 writer.writeStartElement("row");
1103 writer.writeStartElement("heading");
1104 generateFullName(writer, node, relative, marker);
1105 writer.writeEndElement(); // heading
1106
1107 writer.writeStartElement("item");
1108 writer.writeCharacters(node->doc().briefText().toString());
1109 writer.writeEndElement(); // item
1110 writer.writeEndElement(); // row
1111 }
1112 writer.writeEndElement(); // table
1113}
1114
1115void WebXMLGenerator::generateFullName(QXmlStreamWriter &writer,
1116 const Node *apparentNode, const Node *relative, CodeMarker *marker,
1117 const Node *actualNode)
1118{
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
1126}
1127
1128// Classes copied (and slightly adapted) from the HtmlGenerator. These need
1129// refactoring into a common ancestor class.
1130
1131void WebXMLGenerator::findAllClasses(const InnerNode *node)
1132{
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;
1141
1142 QString moduleName = (*c)->moduleName();
1143 if (!moduleName.isEmpty())
1144 moduleClassMap[moduleName].insert((*c)->name(), *c);
1145
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));
1152 }
1153 }
1154 ++c;
1155 }
1156}
1157
1158void WebXMLGenerator::findAllNamespaces(const InnerNode *node)
1159{
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);
1174 }
1175 }
1176 }
1177 }
1178 ++c;
1179 }
1180}
1181
1182const QPair<QString,QString> WebXMLGenerator::anchorForNode(const Node *node)
1183{
1184 QPair<QString,QString> anchorPair;
1185
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();
1190 }
1191
1192 return anchorPair;
1193}
1194
1195QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.