1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 /*
43   ditaxmlgenerator.cpp
44 */
45 
46 #include "codemarker.h"
47 #include "codeparser.h"
48 #include "ditaxmlgenerator.h"
49 #include "node.h"
50 #include "quoter.h"
51 #include "separator.h"
52 #include "tree.h"
53 #include <ctype.h>
54 #include <qdebug.h>
55 #include <qlist.h>
56 #include <qiterator.h>
57 #include <qtextcodec.h>
58 #include <QUuid>
59 
60 QT_BEGIN_NAMESPACE
61 
62 #define COMMAND_VERSION                         Doc::alias("version")
63 int DitaXmlGenerator::id = 0;
64 
65 /*
66   The strings in this array must appear in the same order as
67   the values in enum DitaXmlGenerator::DitaTag.
68  */
69 QString DitaXmlGenerator::ditaTags[] =
70     {
71         "",
72         "alt",
73         "apiDesc",
74         "APIMap",
75         "apiName",
76         "apiRelation",
77         "audience",
78         "author",
79         "b",
80         "body",
81         "bodydiv",
82         "brand",
83         "category",
84         "codeblock",
85         "comment",
86         "component",
87         "copyrholder",
88         "copyright",
89         "copyryear",
90         "created",
91         "critdates",
92         "cxxAPIMap",
93         "cxxClass",
94         "cxxClassAbstract",
95         "cxxClassAccessSpecifier",
96         "cxxClassAPIItemLocation",
97         "cxxClassBaseClass",
98         "cxxClassDeclarationFile",
99         "cxxClassDeclarationFileLine",
100         "cxxClassDefinition",
101         "cxxClassDerivation",
102         "cxxClassDerivationAccessSpecifier",
103         "cxxClassDerivations",
104         "cxxClassDetail",
105         "cxxClassNested",
106         "cxxClassNestedClass",
107         "cxxClassNestedDetail",
108         "cxxDefine",
109         "cxxDefineAccessSpecifier",
110         "cxxDefineAPIItemLocation",
111         "cxxDefineDeclarationFile",
112         "cxxDefineDeclarationFileLine",
113         "cxxDefineDefinition",
114         "cxxDefineDetail",
115         "cxxDefineNameLookup",
116         "cxxDefineParameter",
117         "cxxDefineParameterDeclarationName",
118         "cxxDefineParameters",
119         "cxxDefinePrototype",
120         "cxxDefineReimplemented",
121         "cxxEnumeration",
122         "cxxEnumerationAccessSpecifier",
123         "cxxEnumerationAPIItemLocation",
124         "cxxEnumerationDeclarationFile",
125         "cxxEnumerationDeclarationFileLine",
126         "cxxEnumerationDefinition",
127         "cxxEnumerationDefinitionFile",
128         "cxxEnumerationDefinitionFileLineStart",
129         "cxxEnumerationDefinitionFileLineEnd",
130         "cxxEnumerationDetail",
131         "cxxEnumerationNameLookup",
132         "cxxEnumerationPrototype",
133         "cxxEnumerationScopedName",
134         "cxxEnumerator",
135         "cxxEnumeratorInitialiser",
136         "cxxEnumeratorNameLookup",
137         "cxxEnumeratorPrototype",
138         "cxxEnumerators",
139         "cxxEnumeratorScopedName",
140         "cxxFunction",
141         "cxxFunctionAccessSpecifier",
142         "cxxFunctionAPIItemLocation",
143         "cxxFunctionConst",
144         "cxxFunctionConstructor",
145         "cxxFunctionDeclarationFile",
146         "cxxFunctionDeclarationFileLine",
147         "cxxFunctionDeclaredType",
148         "cxxFunctionDefinition",
149         "cxxFunctionDestructor",
150         "cxxFunctionDetail",
151         "cxxFunctionNameLookup",
152         "cxxFunctionParameter",
153         "cxxFunctionParameterDeclarationName",
154         "cxxFunctionParameterDeclaredType",
155         "cxxFunctionParameterDefaultValue",
156         "cxxFunctionParameters",
157         "cxxFunctionPrototype",
158         "cxxFunctionPureVirtual",
159         "cxxFunctionReimplemented",
160         "cxxFunctionScopedName",
161         "cxxFunctionStorageClassSpecifierStatic",
162         "cxxFunctionVirtual",
163         "cxxTypedef",
164         "cxxTypedefAccessSpecifier",
165         "cxxTypedefAPIItemLocation",
166         "cxxTypedefDeclarationFile",
167         "cxxTypedefDeclarationFileLine",
168         "cxxTypedefDefinition",
169         "cxxTypedefDetail",
170         "cxxTypedefNameLookup",
171         "cxxTypedefScopedName",
172         "cxxVariable",
173         "cxxVariableAccessSpecifier",
174         "cxxVariableAPIItemLocation",
175         "cxxVariableDeclarationFile",
176         "cxxVariableDeclarationFileLine",
177         "cxxVariableDeclaredType",
178         "cxxVariableDefinition",
179         "cxxVariableDetail",
180         "cxxVariableNameLookup",
181         "cxxVariablePrototype",
182         "cxxVariableReimplemented",
183         "cxxVariableScopedName",
184         "cxxVariableStorageClassSpecifierStatic",
185         "data",
186         "data-about",
187         "dd",
188         "dl",
189         "dlentry",
190         "dt",
191         "entry",
192         "fig",
193         "i",
194         "image",
195         "keyword",
196         "keywords",
197         "li",
198         "link",
199         "linktext",
200         "lq",
201         "metadata",
202         "ol",
203         "othermeta",
204         "p",
205         "parameter",
206         "permissions",
207         "ph",
208         "platform",
209         "pre",
210         "prodinfo",
211         "prodname",
212         "prolog",
213         "publisher",
214         "related-links",
215         "resourceid",
216         "revised",
217         "row",
218         "section",
219         "sectiondiv",
220         "shortdesc",
221         "simpletable",
222         "source",
223         "stentry",
224         "sthead",
225         "strow",
226         "sub",
227         "sup",
228         "table",
229         "tbody",
230         "tgroup",
231         "thead",
232         "title",
233         "tm",
234         "topic",
235         "topicmeta",
236         "topicref",
237         "tt",
238         "u",
239         "ul",
240         "unknown",
241         "vrm",
242         "vrmlist",
243         "xref",
244         ""
245     };
246 
247 static bool showBrokenLinks = false;
248 
249 /*!
250   Quick, dirty, and very ugly. Unescape \a text
251   so QXmlStreamWriter::writeCharacters() can put
252   the escapes back in again!
253  */
writeCharacters(const QString & text)254 void DitaXmlGenerator::writeCharacters(const QString& text)
255 {
256     QString t = text;
257     t = t.replace("&lt;","<");
258     t = t.replace("&gt;",">");
259     t = t.replace("&amp;","&");
260     t = t.replace("&quot;","\"");
261     xmlWriter().writeCharacters(t);
262 }
263 
264 /*!
265   Appends an <xref> element to the current XML stream
266   with the \a href attribute and the \a text.
267  */
addLink(const QString & href,const QStringRef & text,DitaTag t)268 void DitaXmlGenerator::addLink(const QString& href,
269                                const QStringRef& text,
270                                DitaTag t)
271 {
272     if (!href.isEmpty()) {
273         writeStartTag(t);
274         // formathtml
275         xmlWriter().writeAttribute("href", href);
276         writeCharacters(text.toString());
277         writeEndTag(); // </t>
278     }
279     else {
280         writeCharacters(text.toString());
281     }
282 }
283 
284 /*!
285   Push \a t onto the dita tag stack and write the appropriate
286   start tag to the DITA XML file.
287  */
writeStartTag(DitaTag t)288 void DitaXmlGenerator::writeStartTag(DitaTag t)
289 {
290     xmlWriter().writeStartElement(ditaTags[t]);
291     tagStack.push(t);
292 }
293 
294 /*!
295   Pop the current DITA tag off the stack, and write the
296   appropriate end tag to the DITA XML file.
297  */
writeEndTag(DitaTag t)298 void DitaXmlGenerator::writeEndTag(DitaTag t)
299 {
300     DitaTag top = tagStack.pop();
301     if (t > DT_NONE && top != t)
302         qDebug() << "Expected:" << t << "ACTUAL:" << top;
303     xmlWriter().writeEndElement();
304 }
305 
306 /*!
307   Return the current DITA element tag, the one
308   on top of the stack.
309  */
currentTag()310 DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
311 {
312     return tagStack.top();
313 }
314 
315 /*!
316   Write the start tag \c{<apiDesc>}. if \a title is not
317   empty, generate a GUID from it and write the GUID as the
318   value of the \e{id} attribute. Then write \a title as
319   the value of the \e {spectitle} attribute.
320 
321   Then if \a outputclass is not empty, write it as the value
322   of the \a outputclass attribute.
323 
324   Fiunally, set the section nesting level to 1 and return 1.
325  */
enterApiDesc(const QString & outputclass,const QString & title)326 int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& title)
327 {
328     writeStartTag(DT_apiDesc);
329     if (!title.isEmpty()) {
330         writeGuidAttribute(title);
331         xmlWriter().writeAttribute("spectitle",title);
332      }
333     if (!outputclass.isEmpty())
334         xmlWriter().writeAttribute("outputclass",outputclass);
335     sectionNestingLevel = 1;
336     return sectionNestingLevel;
337 }
338 
339 /*!
340   If the section nesting level is 0, output a \c{<section>}
341   element with an \e id attribute generated from \a title and
342   an \e outputclass attribute set to \a outputclass.
343   If \a title is null, no \e id attribute is output.
344   If \a outputclass is empty, no \e outputclass attribute
345   is output.
346 
347   Finally, increment the section nesting level and return
348   the new value.
349  */
enterSection(const QString & outputclass,const QString & title)350 int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
351 {
352     if (sectionNestingLevel == 0) {
353         writeStartTag(DT_section);
354         if (!title.isEmpty())
355             writeGuidAttribute(title);
356         if (!outputclass.isEmpty())
357             xmlWriter().writeAttribute("outputclass",outputclass);
358     }
359     else if (!title.isEmpty()) {
360         writeStartTag(DT_p);
361         writeGuidAttribute(title);
362         if (!outputclass.isEmpty())
363             xmlWriter().writeAttribute("outputclass",outputclass);
364         writeCharacters(title);
365         writeEndTag(); // </p>
366     }
367     return ++sectionNestingLevel;
368 }
369 
370 /*!
371   If the section nesting level is greater than 0, decrement
372   it. If it becomes 0, output a \c {</section>}. Return the
373   decremented section nesting level.
374  */
leaveSection()375 int DitaXmlGenerator::leaveSection()
376 {
377     if (sectionNestingLevel > 0) {
378         --sectionNestingLevel;
379         if (sectionNestingLevel == 0)
380             writeEndTag(); // </section> or </apiDesc>
381     }
382     return sectionNestingLevel;
383 }
384 
385 /*!
386   The default constructor.
387  */
DitaXmlGenerator()388 DitaXmlGenerator::DitaXmlGenerator()
389     : inContents(false),
390       inDetailedDescription(false),
391       inLegaleseText(false),
392       inLink(false),
393       inObsoleteLink(false),
394       inSectionHeading(false),
395       inTableHeader(false),
396       inTableBody(false),
397       noLinks(false),
398       obsoleteLinks(false),
399       offlineDocs(true),
400       threeColumnEnumValueTable(true),
401       codeIndent(0),
402       numTableRows(0),
403       divNestingLevel(0),
404       sectionNestingLevel(0),
405       tableColumnCount(0),
406       funcLeftParen("\\S(\\()"),
407       myTree(0)
408 {
409     // nothing yet.
410 }
411 
412 /*!
413   The destructor has nothing to do.
414  */
~DitaXmlGenerator()415 DitaXmlGenerator::~DitaXmlGenerator()
416 {
417     GuidMaps::iterator i = guidMaps.begin();
418     while (i != guidMaps.end()) {
419         delete i.value();
420         ++i;
421     }
422 }
423 
424 /*!
425   A lot of internal structures are initialized.
426  */
initializeGenerator(const Config & config)427 void DitaXmlGenerator::initializeGenerator(const Config &config)
428 {
429     Generator::initializeGenerator(config);
430     obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
431     setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
432 
433     style = config.getString(DitaXmlGenerator::format() +
434                              Config::dot +
435                              DITAXMLGENERATOR_STYLE);
436     postHeader = config.getString(DitaXmlGenerator::format() +
437                                   Config::dot +
438                                   DITAXMLGENERATOR_POSTHEADER);
439     postPostHeader = config.getString(DitaXmlGenerator::format() +
440                                       Config::dot +
441                                       DITAXMLGENERATOR_POSTPOSTHEADER);
442     footer = config.getString(DitaXmlGenerator::format() +
443                               Config::dot +
444                               DITAXMLGENERATOR_FOOTER);
445     address = config.getString(DitaXmlGenerator::format() +
446                                Config::dot +
447                                DITAXMLGENERATOR_ADDRESS);
448     pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
449                                           Config::dot +
450                                           DITAXMLGENERATOR_GENERATEMACREFS);
451 
452     project = config.getString(CONFIG_PROJECT);
453     projectDescription = config.getString(CONFIG_DESCRIPTION);
454     if (projectDescription.isEmpty() && !project.isEmpty())
455         projectDescription = project + " Reference Documentation";
456 
457     projectUrl = config.getString(CONFIG_URL);
458 
459     outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
460     if (outputEncoding.isEmpty())
461         outputEncoding = QLatin1String("ISO-8859-1");
462     outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
463 
464     naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
465     if (naturalLanguage.isEmpty())
466         naturalLanguage = QLatin1String("en");
467 
468     config.subVarsAndValues("dita.metadata.default",metadataDefaults);
469     QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
470     QSet<QString>::ConstIterator edition = editionNames.begin();
471     while (edition != editionNames.end()) {
472         QString editionName = *edition;
473         QStringList editionModules = config.getStringList(CONFIG_EDITION +
474                                                           Config::dot +
475                                                           editionName +
476                                                           Config::dot +
477                                                           "modules");
478         QStringList editionGroups = config.getStringList(CONFIG_EDITION +
479                                                          Config::dot +
480                                                          editionName +
481                                                          Config::dot +
482                                                          "groups");
483 
484         if (!editionModules.isEmpty())
485             editionModuleMap[editionName] = editionModules;
486         if (!editionGroups.isEmpty())
487             editionGroupMap[editionName] = editionGroups;
488 
489         ++edition;
490     }
491 
492     stylesheets = config.getStringList(DitaXmlGenerator::format() +
493                                        Config::dot +
494                                        DITAXMLGENERATOR_STYLESHEETS);
495     customHeadElements = config.getStringList(DitaXmlGenerator::format() +
496                                               Config::dot +
497                                               DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
498     codeIndent = config.getInt(CONFIG_CODEINDENT);
499     version = config.getString(CONFIG_VERSION);
500     vrm = version.split(".");
501 }
502 
503 /*!
504   All this does is call the same function in the base class.
505  */
terminateGenerator()506 void DitaXmlGenerator::terminateGenerator()
507 {
508     Generator::terminateGenerator();
509 }
510 
511 /*!
512   Returns "DITAXML".
513  */
format()514 QString DitaXmlGenerator::format()
515 {
516     return "DITAXML";
517 }
518 
519 /*!
520   Calls lookupGuid() to get a GUID for \a text, then writes
521   it to the XML stream as an "id" attribute, and returns it.
522  */
writeGuidAttribute(QString text)523 QString DitaXmlGenerator::writeGuidAttribute(QString text)
524 {
525     QString guid = lookupGuid(outFileName(),text);
526     xmlWriter().writeAttribute("id",guid);
527     return guid;
528 }
529 
530 
531 /*!
532   Write's the GUID for the \a node to the current XML stream
533   as an "id" attribute. If the \a node doesn't yet have a GUID,
534   one is generated.
535  */
writeGuidAttribute(Node * node)536 void DitaXmlGenerator::writeGuidAttribute(Node* node)
537 {
538     xmlWriter().writeAttribute("id",node->guid());
539 }
540 
541 /*!
542   Looks up \a text in the GUID map. If it finds \a text,
543   it returns the associated GUID. Otherwise it inserts
544   \a text into the map with a new GUID, and it returns
545   the new GUID.
546  */
lookupGuid(QString text)547 QString DitaXmlGenerator::lookupGuid(QString text)
548 {
549     QMap<QString, QString>::const_iterator i = name2guidMap.find(text);
550     if (i != name2guidMap.end())
551         return i.value();
552     QString t = QUuid::createUuid().toString();
553     QString guid = "id-" + t.mid(1,t.length()-2);
554     name2guidMap.insert(text,guid);
555     return guid;
556 }
557 
558 /*!
559   First, look up the GUID map for \a fileName. If there isn't
560   a GUID map for \a fileName, create one and insert it into
561   the map of GUID maps. Then look up \a text in that GUID map.
562   If \a text is found, return the associated GUID. Otherwise,
563   insert \a text into the GUID map with a new GUID, and return
564   the new GUID.
565  */
lookupGuid(const QString & fileName,const QString & text)566 QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
567 {
568     GuidMap* gm = lookupGuidMap(fileName);
569     GuidMap::const_iterator i = gm->find(text);
570     if (i != gm->end())
571         return i.value();
572     QString t = QUuid::createUuid().toString();
573     QString guid = "id-" + t.mid(1,t.length()-2);
574     gm->insert(text,guid);
575     return guid;
576 }
577 
578 /*!
579   Looks up \a fileName in the map of GUID maps. If it finds
580   \a fileName, it returns a pointer to the associated GUID
581   map. Otherwise it creates a new GUID map and inserts it
582   into the map of GUID maps with \a fileName as its key.
583  */
lookupGuidMap(const QString & fileName)584 GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
585 {
586     GuidMaps::const_iterator i = guidMaps.find(fileName);
587     if (i != guidMaps.end())
588         return i.value();
589     GuidMap* gm = new GuidMap;
590     guidMaps.insert(fileName,gm);
591     return gm;
592 }
593 
594 /*!
595   This is where the DITA XML files are written.
596   \note The file generation is done in the base class,
597   PageGenerator::generateTree().
598  */
generateTree(const Tree * tree)599 void DitaXmlGenerator::generateTree(const Tree *tree)
600 {
601     myTree = tree;
602     nonCompatClasses.clear();
603     mainClasses.clear();
604     compatClasses.clear();
605     obsoleteClasses.clear();
606     moduleClassMap.clear();
607     moduleNamespaceMap.clear();
608     funcIndex.clear();
609     legaleseTexts.clear();
610     serviceClasses.clear();
611     qmlClasses.clear();
612     findAllClasses(tree->root());
613     findAllFunctions(tree->root());
614     findAllLegaleseTexts(tree->root());
615     findAllNamespaces(tree->root());
616     findAllSince(tree->root());
617 
618     PageGenerator::generateTree(tree);
619     writeDitaMap();
620 }
621 
startText(const Node *,CodeMarker *)622 void DitaXmlGenerator::startText(const Node* /* relative */,
623                                  CodeMarker* /* marker */)
624 {
625     inLink = false;
626     inContents = false;
627     inSectionHeading = false;
628     inTableHeader = false;
629     numTableRows = 0;
630     threeColumnEnumValueTable = true;
631     link.clear();
632     sectionNumber.clear();
633 }
634 
countTableColumns(const Atom * t)635 static int countTableColumns(const Atom* t)
636 {
637     int result = 0;
638     if (t->type() == Atom::TableHeaderLeft) {
639         while (t->type() == Atom::TableHeaderLeft) {
640             int count = 0;
641             t = t->next();
642             while (t->type() != Atom::TableHeaderRight) {
643                 if (t->type() == Atom::TableItemLeft)
644                     ++count;
645                 t = t->next();
646             }
647             if (count > result)
648                 result = count;
649             t = t->next();
650         }
651     }
652     else if (t->type() == Atom::TableRowLeft) {
653         while (t->type() != Atom::TableRowRight) {
654             if (t->type() == Atom::TableItemLeft)
655                 ++result;
656             t = t->next();
657         }
658     }
659     return result;
660 }
661 
662 /*!
663   Generate html from an instance of Atom.
664  */
generateAtom(const Atom * atom,const Node * relative,CodeMarker * marker)665 int DitaXmlGenerator::generateAtom(const Atom *atom,
666                                    const Node *relative,
667                                    CodeMarker *marker)
668 {
669     int skipAhead = 0;
670     QString hx, str;
671     static bool in_para = false;
672     QString guid, hc, attr;
673 
674     switch (atom->type()) {
675     case Atom::AbstractLeft:
676         break;
677     case Atom::AbstractRight:
678         break;
679     case Atom::AutoLink:
680         if (!noLinks && !inLink && !inContents && !inSectionHeading) {
681             const Node* node = 0;
682             QString link = getLink(atom, relative, marker, &node);
683             if (!link.isEmpty()) {
684                 beginLink(link);
685                 generateLink(atom, relative, marker);
686                 endLink();
687             }
688             else {
689                 writeCharacters(protectEnc(atom->string()));
690             }
691         }
692         else {
693             writeCharacters(protectEnc(atom->string()));
694         }
695         break;
696     case Atom::BaseName:
697         break;
698     case Atom::BriefLeft:
699         //if (relative->type() == Node::Fake) {
700         //skipAhead = skipAtoms(atom, Atom::BriefRight);
701         //break;
702         //}
703         if (inSection()) {
704             writeStartTag(DT_p);
705             xmlWriter().writeAttribute("outputclass","brief");
706         }
707         else {
708             noLinks = true;
709             writeStartTag(DT_shortdesc);
710         }
711         if (relative->type() == Node::Property ||
712             relative->type() == Node::Variable) {
713             xmlWriter().writeCharacters("This ");
714             if (relative->type() == Node::Property)
715                 xmlWriter().writeCharacters("property");
716             else if (relative->type() == Node::Variable)
717                 xmlWriter().writeCharacters("variable");
718             xmlWriter().writeCharacters(" holds ");
719         }
720         if (noLinks) {
721             atom = atom->next();
722             while (atom != 0 && atom->type() != Atom::BriefRight) {
723                 if (atom->type() == Atom::String ||
724                     atom->type() == Atom::AutoLink)
725                     str += atom->string();
726                 skipAhead++;
727                 atom = atom->next();
728             }
729             str[0] = str[0].toLower();
730             if (str.right(1) == ".")
731                 str.truncate(str.length() - 1);
732             writeCharacters(str + ".");
733         }
734         break;
735     case Atom::BriefRight:
736         //        if (relative->type() != Node::Fake)
737         writeEndTag(); // </shortdesc> or </p>
738         noLinks = false;
739         break;
740     case Atom::C:
741         writeStartTag(DT_tt);
742         if (inLink) {
743             writeCharacters(protectEnc(plainCode(atom->string())));
744         }
745         else {
746             writeText(atom->string(), marker, relative);
747         }
748         writeEndTag(); // see writeStartElement() above
749         break;
750     case Atom::Code:
751         {
752             writeStartTag(DT_codeblock);
753             xmlWriter().writeAttribute("outputclass","cpp");
754             QString chars = trimmedTrailing(atom->string());
755             writeText(chars, marker, relative);
756             writeEndTag(); // </codeblock>
757         }
758 	break;
759     case Atom::Qml:
760         writeStartTag(DT_codeblock);
761         xmlWriter().writeAttribute("outputclass","qml");
762         writeText(trimmedTrailing(atom->string()), marker, relative);
763         writeEndTag(); // </codeblock>
764 	break;
765     case Atom::CodeNew:
766         writeStartTag(DT_p);
767         xmlWriter().writeCharacters("you can rewrite it as");
768         writeEndTag(); // </p>
769         writeStartTag(DT_codeblock);
770         writeText(trimmedTrailing(atom->string()), marker, relative);
771         writeEndTag(); // </codeblock>
772         break;
773     case Atom::CodeOld:
774         writeStartTag(DT_p);
775         xmlWriter().writeCharacters("For example, if you have code like");
776         writeEndTag(); // </p>
777         // fallthrough
778     case Atom::CodeBad:
779         writeStartTag(DT_codeblock);
780         writeCharacters(trimmedTrailing(plainCode(atom->string())));
781         writeEndTag(); // </codeblock>
782 	break;
783     case Atom::DivLeft:
784         {
785             bool inStartElement = false;
786             attr = atom->string();
787             DitaTag t = currentTag();
788             if ((t == DT_section) || (t == DT_sectiondiv)) {
789                 writeStartTag(DT_sectiondiv);
790                 divNestingLevel++;
791                 inStartElement = true;
792             }
793             else if ((t == DT_body) || (t == DT_bodydiv)) {
794                 writeStartTag(DT_bodydiv);
795                 divNestingLevel++;
796                 inStartElement = true;
797             }
798             if (!attr.isEmpty()) {
799                 if (attr.contains('=')) {
800                     int index = 0;
801                     int from = 0;
802                     QString values;
803                     while (index >= 0) {
804                         index = attr.indexOf('"',from);
805                         if (index >= 0) {
806                             ++index;
807                             from = index;
808                             index = attr.indexOf('"',from);
809                             if (index > from) {
810                                 if (!values.isEmpty())
811                                     values.append(' ');
812                                 values += attr.mid(from,index-from);
813                                 from = index+1;
814                             }
815                         }
816                     }
817                     attr = values;
818                 }
819             }
820             if (inStartElement)
821                 xmlWriter().writeAttribute("outputclass", attr);
822         }
823         break;
824     case Atom::DivRight:
825         if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
826             writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
827             if (divNestingLevel > 0)
828                 --divNestingLevel;
829         }
830         break;
831     case Atom::FootnoteLeft:
832         // ### For now
833         if (in_para) {
834             writeEndTag(); // </p>
835             in_para = false;
836         }
837         xmlWriter().writeCharacters("<!-- ");
838         break;
839     case Atom::FootnoteRight:
840         // ### For now
841         xmlWriter().writeCharacters("-->");
842         break;
843     case Atom::FormatElse:
844     case Atom::FormatEndif:
845     case Atom::FormatIf:
846         break;
847     case Atom::FormattingLeft:
848         {
849             DitaTag t = DT_LAST;
850             if (atom->string() == ATOM_FORMATTING_BOLD)
851                 t = DT_b;
852             else if (atom->string() == ATOM_FORMATTING_PARAMETER)
853                 t = DT_i;
854             else if (atom->string() == ATOM_FORMATTING_ITALIC)
855                 t = DT_i;
856             else if (atom->string() == ATOM_FORMATTING_TELETYPE)
857                 t = DT_tt;
858             else if (atom->string().startsWith("span ")) {
859                 t = DT_keyword;
860             }
861             else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
862                 t = DT_u;
863             else if (atom->string() == ATOM_FORMATTING_INDEX)
864                 t = DT_comment;
865             else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
866                 t = DT_sub;
867             else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
868                 t = DT_sup;
869             else
870                 qDebug() << "DT_LAST";
871             writeStartTag(t);
872             if (atom->string() == ATOM_FORMATTING_PARAMETER) {
873                 if (atom->next() != 0 && atom->next()->type() == Atom::String) {
874                     QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
875                     if (subscriptRegExp.exactMatch(atom->next()->string())) {
876                         xmlWriter().writeCharacters(subscriptRegExp.cap(1));
877                         writeStartTag(DT_sub);
878                         xmlWriter().writeCharacters(subscriptRegExp.cap(2));
879                         writeEndTag(); // </sub>
880                         skipAhead = 1;
881                     }
882                 }
883             }
884             else if (t == DT_keyword) {
885                 QString attr = atom->string().mid(5);
886                 if (!attr.isEmpty()) {
887                     if (attr.contains('=')) {
888                         int index = 0;
889                         int from = 0;
890                         QString values;
891                         while (index >= 0) {
892                             index = attr.indexOf('"',from);
893                             if (index >= 0) {
894                                 ++index;
895                                 from = index;
896                                 index = attr.indexOf('"',from);
897                                 if (index > from) {
898                                     if (!values.isEmpty())
899                                         values.append(' ');
900                                     values += attr.mid(from,index-from);
901                                     from = index+1;
902                                 }
903                             }
904                         }
905                         attr = values;
906                     }
907                 }
908                 xmlWriter().writeAttribute("outputclass", attr);
909             }
910         }
911         break;
912     case Atom::FormattingRight:
913         if (atom->string() == ATOM_FORMATTING_LINK) {
914             endLink();
915         }
916         else {
917             writeEndTag(); // ?
918         }
919         break;
920     case Atom::AnnotatedList:
921         {
922             QList<Node*> values = myTree->groups().values(atom->string());
923             NodeMap nodeMap;
924             for (int i = 0; i < values.size(); ++i) {
925                 const Node* n = values.at(i);
926                 if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
927                     nodeMap.insert(n->nameForLists(),n);
928                 }
929             }
930             generateAnnotatedList(relative, marker, nodeMap);
931         }
932         break;
933     case Atom::GeneratedList:
934         if (atom->string() == "annotatedclasses") {
935             generateAnnotatedList(relative, marker, nonCompatClasses);
936         }
937         else if (atom->string() == "classes") {
938             generateCompactList(relative, marker, nonCompatClasses, true);
939         }
940         else if (atom->string() == "qmlclasses") {
941             generateCompactList(relative, marker, qmlClasses, true);
942         }
943         else if (atom->string().contains("classesbymodule")) {
944             QString arg = atom->string().trimmed();
945             QString moduleName = atom->string().mid(atom->string().indexOf(
946                 "classesbymodule") + 15).trimmed();
947             if (moduleClassMap.contains(moduleName))
948                 generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
949         }
950         else if (atom->string().contains("classesbyedition")) {
951 
952             QString arg = atom->string().trimmed();
953             QString editionName = atom->string().mid(atom->string().indexOf(
954                 "classesbyedition") + 16).trimmed();
955 
956             if (editionModuleMap.contains(editionName)) {
957 
958                 // Add all classes in the modules listed for that edition.
959                 NodeMap editionClasses;
960                 foreach (const QString &moduleName, editionModuleMap[editionName]) {
961                     if (moduleClassMap.contains(moduleName))
962                         editionClasses.unite(moduleClassMap[moduleName]);
963                 }
964 
965                 // Add additional groups and remove groups of classes that
966                 // should be excluded from the edition.
967 
968                 QMultiMap <QString, Node *> groups = myTree->groups();
969                 foreach (const QString &groupName, editionGroupMap[editionName]) {
970                     QList<Node *> groupClasses;
971                     if (groupName.startsWith("-")) {
972                         groupClasses = groups.values(groupName.mid(1));
973                         foreach (const Node *node, groupClasses)
974                             editionClasses.remove(node->name());
975                     }
976                     else {
977                         groupClasses = groups.values(groupName);
978                         foreach (const Node *node, groupClasses)
979                             editionClasses.insert(node->name(), node);
980                     }
981                 }
982                 generateAnnotatedList(relative, marker, editionClasses);
983             }
984         }
985         else if (atom->string() == "classhierarchy") {
986             generateClassHierarchy(relative, marker, nonCompatClasses);
987         }
988         else if (atom->string() == "compatclasses") {
989             generateCompactList(relative, marker, compatClasses, false);
990         }
991         else if (atom->string() == "obsoleteclasses") {
992             generateCompactList(relative, marker, obsoleteClasses, false);
993         }
994         else if (atom->string() == "functionindex") {
995             generateFunctionIndex(relative, marker);
996         }
997         else if (atom->string() == "legalese") {
998             generateLegaleseList(relative, marker);
999         }
1000         else if (atom->string() == "mainclasses") {
1001             generateCompactList(relative, marker, mainClasses, true);
1002         }
1003         else if (atom->string() == "services") {
1004             generateCompactList(relative, marker, serviceClasses, false);
1005         }
1006         else if (atom->string() == "overviews") {
1007             generateOverviewList(relative, marker);
1008         }
1009         else if (atom->string() == "namespaces") {
1010             generateAnnotatedList(relative, marker, namespaceIndex);
1011         }
1012         else if (atom->string() == "related") {
1013             const FakeNode *fake = static_cast<const FakeNode *>(relative);
1014             if (fake && !fake->groupMembers().isEmpty()) {
1015                 NodeMap groupMembersMap;
1016                 foreach (const Node *node, fake->groupMembers()) {
1017                     if (node->type() == Node::Fake)
1018                         groupMembersMap[fullName(node, relative, marker)] = node;
1019                 }
1020                 generateAnnotatedList(fake, marker, groupMembersMap);
1021             }
1022         }
1023         break;
1024     case Atom::SinceList:
1025         {
1026             NewSinceMaps::const_iterator nsmap;
1027             nsmap = newSinceMaps.find(atom->string());
1028             NewClassMaps::const_iterator ncmap;
1029             ncmap = newClassMaps.find(atom->string());
1030             NewClassMaps::const_iterator nqcmap;
1031             nqcmap = newQmlClassMaps.find(atom->string());
1032             if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
1033                 QList<Section> sections;
1034                 QList<Section>::ConstIterator s;
1035                 for (int i=0; i<LastSinceType; ++i)
1036                     sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
1037 
1038                 NodeMultiMap::const_iterator n = nsmap.value().constBegin();
1039                 while (n != nsmap.value().constEnd()) {
1040                     const Node* node = n.value();
1041                     switch (node->type()) {
1042                       case Node::Fake:
1043                           if (node->subType() == Node::QmlClass) {
1044                               sections[QmlClass].appendMember((Node*)node);
1045                           }
1046                           break;
1047                       case Node::Namespace:
1048                           sections[Namespace].appendMember((Node*)node);
1049                           break;
1050                       case Node::Class:
1051                           sections[Class].appendMember((Node*)node);
1052                           break;
1053                       case Node::Enum:
1054                           sections[Enum].appendMember((Node*)node);
1055                           break;
1056                       case Node::Typedef:
1057                           sections[Typedef].appendMember((Node*)node);
1058                           break;
1059                       case Node::Function: {
1060                           const FunctionNode* fn = static_cast<const FunctionNode*>(node);
1061                           if (fn->isMacro())
1062                               sections[Macro].appendMember((Node*)node);
1063                           else {
1064                               Node* p = fn->parent();
1065                               if (p) {
1066                                   if (p->type() == Node::Class)
1067                                       sections[MemberFunction].appendMember((Node*)node);
1068                                   else if (p->type() == Node::Namespace) {
1069                                       if (p->name().isEmpty())
1070                                           sections[GlobalFunction].appendMember((Node*)node);
1071                                       else
1072                                           sections[NamespaceFunction].appendMember((Node*)node);
1073                                   }
1074                                   else
1075                                       sections[GlobalFunction].appendMember((Node*)node);
1076                               }
1077                               else
1078                                   sections[GlobalFunction].appendMember((Node*)node);
1079                           }
1080                           break;
1081                       }
1082                       case Node::Property:
1083                           sections[Property].appendMember((Node*)node);
1084                           break;
1085                       case Node::Variable:
1086                           sections[Variable].appendMember((Node*)node);
1087                           break;
1088                       case Node::QmlProperty:
1089                           sections[QmlProperty].appendMember((Node*)node);
1090                           break;
1091                       case Node::QmlSignal:
1092                           sections[QmlSignal].appendMember((Node*)node);
1093                           break;
1094                       case Node::QmlMethod:
1095                           sections[QmlMethod].appendMember((Node*)node);
1096                           break;
1097                       default:
1098                           break;
1099                     }
1100                     ++n;
1101                 }
1102 
1103                 /*
1104                   First generate the table of contents.
1105                  */
1106                 writeStartTag(DT_ul);
1107                 s = sections.constBegin();
1108                 while (s != sections.constEnd()) {
1109                     if (!(*s).members.isEmpty()) {
1110                         QString li = outFileName() + "#" + Doc::canonicalTitle((*s).name);
1111                         writeXrefListItem(li, (*s).name);
1112                     }
1113                     ++s;
1114                 }
1115                 writeEndTag(); // </ul>
1116 
1117                 int idx = 0;
1118                 s = sections.constBegin();
1119                 while (s != sections.constEnd()) {
1120                     if (!(*s).members.isEmpty()) {
1121                         writeStartTag(DT_p);
1122                         writeGuidAttribute(Doc::canonicalTitle((*s).name));
1123                         xmlWriter().writeAttribute("outputclass","h3");
1124                         writeCharacters(protectEnc((*s).name));
1125                         writeEndTag(); // </p>
1126                         if (idx == Class)
1127                             generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
1128                         else if (idx == QmlClass)
1129                             generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
1130                         else if (idx == MemberFunction) {
1131                             ParentMaps parentmaps;
1132                             ParentMaps::iterator pmap;
1133                             NodeList::const_iterator i = s->members.constBegin();
1134                             while (i != s->members.constEnd()) {
1135                                 Node* p = (*i)->parent();
1136                                 pmap = parentmaps.find(p);
1137                                 if (pmap == parentmaps.end())
1138                                     pmap = parentmaps.insert(p,NodeMultiMap());
1139                                 pmap->insert((*i)->name(),(*i));
1140                                 ++i;
1141                             }
1142                             pmap = parentmaps.begin();
1143                             while (pmap != parentmaps.end()) {
1144                                 NodeList nlist = pmap->values();
1145                                 writeStartTag(DT_p);
1146                                 xmlWriter().writeCharacters("Class ");
1147                                 writeStartTag(DT_xref);
1148                                 // formathtml
1149                                 xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
1150                                 QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
1151                                 writeCharacters(protectEnc(pieces.last()));
1152                                 writeEndTag(); // </xref>
1153                                 xmlWriter().writeCharacters(":");
1154                                 writeEndTag(); // </p>
1155 
1156                                 generateSection(nlist, 0, marker, CodeMarker::Summary);
1157                                 ++pmap;
1158                             }
1159                         }
1160                         else {
1161                             generateSection(s->members, 0, marker, CodeMarker::Summary);
1162                         }
1163                      }
1164                     ++idx;
1165                     ++s;
1166                 }
1167             }
1168         }
1169         break;
1170     case Atom::Image:
1171     case Atom::InlineImage:
1172         {
1173             QString fileName = imageFileName(relative, atom->string());
1174             QString text;
1175             if (atom->next() != 0)
1176                 text = atom->next()->string();
1177             if (fileName.isEmpty()) {
1178                 /*
1179                   Don't bother outputting an error message.
1180                   Just output the href as if the image is in
1181                   the images directory...
1182                  */
1183                 if (atom->string()[0] == '/')
1184                     fileName = QLatin1String("images") + atom->string();
1185                 else
1186                     fileName = QLatin1String("images/") + atom->string();
1187             }
1188 
1189             if (currentTag() != DT_xref)
1190                 writeStartTag(DT_fig);
1191             writeStartTag(DT_image);
1192             xmlWriter().writeAttribute("href",protectEnc(fileName));
1193             if (atom->type() == Atom::InlineImage)
1194                 xmlWriter().writeAttribute("placement","inline");
1195             else {
1196                 xmlWriter().writeAttribute("placement","break");
1197                 xmlWriter().writeAttribute("align","center");
1198             }
1199             if (!text.isEmpty()) {
1200                 writeStartTag(DT_alt);
1201                 writeCharacters(protectEnc(text));
1202                 writeEndTag(); // </alt>
1203             }
1204             writeEndTag(); // </image>
1205             if (currentTag() != DT_xref)
1206                 writeEndTag(); // </fig>
1207         }
1208         break;
1209     case Atom::ImageText:
1210         // nothing
1211         break;
1212     case Atom::LegaleseLeft:
1213         inLegaleseText = true;
1214         break;
1215     case Atom::LegaleseRight:
1216         inLegaleseText = false;
1217         break;
1218     case Atom::LineBreak:
1219         //xmlWriter().writeEmptyElement("br");
1220         break;
1221     case Atom::Link:
1222         {
1223             const Node *node = 0;
1224             QString myLink = getLink(atom, relative, marker, &node);
1225             if (myLink.isEmpty()) {
1226                 relative->doc().location().warning(tr("Can't link to '%1' in %2")
1227                         .arg(atom->string())
1228                         .arg(marker->plainFullName(relative)));
1229             }
1230             else if (!inSectionHeading) {
1231                 beginLink(myLink);
1232             }
1233 #if 0
1234             else {
1235                 //xmlWriter().writeCharacters(atom->string());
1236                 //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string();
1237             }
1238 #endif
1239             skipAhead = 1;
1240         }
1241         break;
1242     case Atom::GuidLink:
1243         {
1244             beginLink(atom->string());
1245             skipAhead = 1;
1246         }
1247         break;
1248     case Atom::LinkNode:
1249         {
1250             const Node* node = CodeMarker::nodeForString(atom->string());
1251             beginLink(linkForNode(node, relative));
1252             skipAhead = 1;
1253         }
1254         break;
1255     case Atom::ListLeft:
1256         if (in_para) {
1257             writeEndTag(); // </p>
1258             in_para = false;
1259         }
1260         if (atom->string() == ATOM_LIST_BULLET) {
1261             writeStartTag(DT_ul);
1262         }
1263         else if (atom->string() == ATOM_LIST_TAG) {
1264             writeStartTag(DT_dl);
1265         }
1266         else if (atom->string() == ATOM_LIST_VALUE) {
1267             threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
1268             if (threeColumnEnumValueTable) {
1269                 writeStartTag(DT_simpletable);
1270                 xmlWriter().writeAttribute("outputclass","valuelist");
1271                 writeStartTag(DT_sthead);
1272                 writeStartTag(DT_stentry);
1273                 xmlWriter().writeCharacters("Constant");
1274                 writeEndTag(); // </stentry>
1275                 writeStartTag(DT_stentry);
1276                 xmlWriter().writeCharacters("Value");
1277                 writeEndTag(); // </stentry>
1278                 writeStartTag(DT_stentry);
1279                 xmlWriter().writeCharacters("Description");
1280                 writeEndTag(); // </stentry>
1281                 writeEndTag(); // </sthead>
1282             }
1283             else {
1284                 writeStartTag(DT_simpletable);
1285                 xmlWriter().writeAttribute("outputclass","valuelist");
1286                 writeStartTag(DT_sthead);
1287                 writeStartTag(DT_stentry);
1288                 xmlWriter().writeCharacters("Constant");
1289                 writeEndTag(); // </stentry>
1290                 writeStartTag(DT_stentry);
1291                 xmlWriter().writeCharacters("Value");
1292                 writeEndTag(); // </stentry>
1293                 writeEndTag(); // </sthead>
1294             }
1295         }
1296         else {
1297             writeStartTag(DT_ol);
1298             if (atom->string() == ATOM_LIST_UPPERALPHA)
1299                 xmlWriter().writeAttribute("outputclass","upperalpha");
1300             else if (atom->string() == ATOM_LIST_LOWERALPHA)
1301                 xmlWriter().writeAttribute("outputclass","loweralpha");
1302             else if (atom->string() == ATOM_LIST_UPPERROMAN)
1303                 xmlWriter().writeAttribute("outputclass","upperroman");
1304             else if (atom->string() == ATOM_LIST_LOWERROMAN)
1305                 xmlWriter().writeAttribute("outputclass","lowerroman");
1306             else // (atom->string() == ATOM_LIST_NUMERIC)
1307                 xmlWriter().writeAttribute("outputclass","numeric");
1308             if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
1309                 // I don't think this attribute is supported.
1310                 xmlWriter().writeAttribute("start",atom->next()->string());
1311             }
1312         }
1313         break;
1314     case Atom::ListItemNumber:
1315         // nothing
1316         break;
1317     case Atom::ListTagLeft:
1318         if (atom->string() == ATOM_LIST_TAG) {
1319             writeStartTag(DT_dt);
1320         }
1321         else { // (atom->string() == ATOM_LIST_VALUE)
1322             writeStartTag(DT_strow);
1323             writeStartTag(DT_stentry);
1324             writeStartTag(DT_tt);
1325             writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
1326                                                                            relative))));
1327             writeEndTag(); // </tt>
1328             writeEndTag(); // </stentry>
1329             writeStartTag(DT_stentry);
1330 
1331             QString itemValue;
1332             if (relative->type() == Node::Enum) {
1333                 const EnumNode *enume = static_cast<const EnumNode *>(relative);
1334                 itemValue = enume->itemValue(atom->next()->string());
1335             }
1336 
1337             if (itemValue.isEmpty())
1338                 xmlWriter().writeCharacters("?");
1339             else {
1340                 writeStartTag(DT_tt);
1341                 writeCharacters(protectEnc(itemValue));
1342                 writeEndTag(); // </tt>
1343             }
1344             skipAhead = 1;
1345         }
1346         break;
1347     case Atom::ListTagRight:
1348         if (atom->string() == ATOM_LIST_TAG)
1349             writeEndTag(); // </dt>
1350         break;
1351     case Atom::ListItemLeft:
1352         if (atom->string() == ATOM_LIST_TAG) {
1353             writeStartTag(DT_dd);
1354         }
1355         else if (atom->string() == ATOM_LIST_VALUE) {
1356             if (threeColumnEnumValueTable) {
1357                 writeEndTag(); // </stentry>
1358                 writeStartTag(DT_stentry);
1359             }
1360         }
1361         else {
1362             writeStartTag(DT_li);
1363         }
1364         if (matchAhead(atom, Atom::ParaLeft))
1365             skipAhead = 1;
1366         break;
1367     case Atom::ListItemRight:
1368         if (atom->string() == ATOM_LIST_TAG) {
1369             writeEndTag(); // </dd>
1370         }
1371         else if (atom->string() == ATOM_LIST_VALUE) {
1372             writeEndTag(); // </stentry>
1373             writeEndTag(); // </strow>
1374         }
1375         else {
1376             writeEndTag(); // </li>
1377         }
1378         break;
1379     case Atom::ListRight:
1380         if (atom->string() == ATOM_LIST_BULLET) {
1381             writeEndTag(); // </ul>
1382         }
1383         else if (atom->string() == ATOM_LIST_TAG) {
1384             writeEndTag(); // </dl>
1385         }
1386         else if (atom->string() == ATOM_LIST_VALUE) {
1387             writeEndTag(); // </simpletable>
1388         }
1389         else {
1390             writeEndTag(); // </ol>
1391         }
1392         break;
1393     case Atom::Nop:
1394         // nothing
1395         break;
1396     case Atom::ParaLeft:
1397         writeStartTag(DT_p);
1398         if (inLegaleseText)
1399             xmlWriter().writeAttribute("outputclass","legalese");
1400         in_para = true;
1401         break;
1402     case Atom::ParaRight:
1403         endLink();
1404         if (in_para) {
1405             writeEndTag(); // </p>
1406             in_para = false;
1407         }
1408         break;
1409     case Atom::QuotationLeft:
1410         writeStartTag(DT_lq);
1411         break;
1412     case Atom::QuotationRight:
1413         writeEndTag(); // </lq>
1414         break;
1415     case Atom::RawString:
1416         if (atom->string() == " ")
1417             break;
1418         if (atom->string().startsWith("&"))
1419             writeCharacters(atom->string());
1420         else if (atom->string() == "<sup>*</sup>") {
1421             writeStartTag(DT_sup);
1422             writeCharacters("*");
1423             writeEndTag(); // </sup>
1424         }
1425         else if (atom->string() == "<sup>&reg;</sup>") {
1426             writeStartTag(DT_tm);
1427             xmlWriter().writeAttribute("tmtype","reg");
1428             writeEndTag(); // </tm>
1429         }
1430         else {
1431             writeStartTag(DT_pre);
1432             xmlWriter().writeAttribute("outputclass","raw-html");
1433             writeCharacters(atom->string());
1434             writeEndTag(); // </pre>
1435         }
1436         break;
1437     case Atom::SectionLeft:
1438 #if 0
1439         if (inApiDesc) {
1440             writeEndTag(); // </apiDesc>
1441             inApiDesc = false;
1442         }
1443 #endif
1444         enterSection("details",QString());
1445         //writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
1446         break;
1447     case Atom::SectionRight:
1448         leaveSection();
1449         break;
1450     case Atom::SectionHeadingLeft:
1451         writeStartTag(DT_p);
1452         writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
1453         hx = "h" + QString::number(atom->string().toInt() + hOffset(relative));
1454         xmlWriter().writeAttribute("outputclass",hx);
1455         inSectionHeading = true;
1456         break;
1457     case Atom::SectionHeadingRight:
1458         writeEndTag(); // </title> (see case Atom::SectionHeadingLeft)
1459         inSectionHeading = false;
1460         break;
1461     case Atom::SidebarLeft:
1462         // nothing
1463         break;
1464     case Atom::SidebarRight:
1465         // nothing
1466         break;
1467     case Atom::String:
1468         if (inLink && !inContents && !inSectionHeading) {
1469             generateLink(atom, relative, marker);
1470         }
1471         else {
1472             writeCharacters(atom->string());
1473         }
1474         break;
1475     case Atom::TableLeft:
1476         {
1477             if (in_para) {
1478                 writeEndTag(); // </p>
1479                 in_para = false;
1480             }
1481             writeStartTag(DT_table);
1482             numTableRows = 0;
1483             if (tableColumnCount != 0) {
1484                 qDebug() << "ERROR: Nested tables!";
1485                 tableColumnCount = 0;
1486             }
1487             tableColumnCount = countTableColumns(atom->next());
1488             writeStartTag(DT_tgroup);
1489             xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1490             inTableHeader = false;
1491             inTableBody = false;
1492         }
1493         break;
1494     case Atom::TableRight:
1495         writeEndTag(); // </tbody>
1496         writeEndTag(); // </tgroup>
1497         writeEndTag(); // </table>
1498         inTableHeader = false;
1499         inTableBody = false;
1500         tableColumnCount = 0;
1501         break;
1502     case Atom::TableHeaderLeft:
1503         if (inTableBody) {
1504             writeEndTag(); // </tbody>
1505             writeEndTag(); // </tgroup>
1506             writeEndTag(); // </table>
1507             inTableHeader = false;
1508             inTableBody = false;
1509             tableColumnCount = 0;
1510             writeStartTag(DT_table);
1511             numTableRows = 0;
1512             tableColumnCount = countTableColumns(atom);
1513             writeStartTag(DT_tgroup);
1514             xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
1515         }
1516         writeStartTag(DT_thead);
1517         xmlWriter().writeAttribute("valign","top");
1518         writeStartTag(DT_row);
1519         xmlWriter().writeAttribute("valign","top");
1520         inTableHeader = true;
1521         inTableBody = false;
1522         break;
1523     case Atom::TableHeaderRight:
1524         writeEndTag(); // </row>
1525         if (matchAhead(atom, Atom::TableHeaderLeft)) {
1526             skipAhead = 1;
1527             writeStartTag(DT_row);
1528             xmlWriter().writeAttribute("valign","top");
1529         }
1530         else {
1531             writeEndTag(); // </thead>
1532             inTableHeader = false;
1533             inTableBody = true;
1534             writeStartTag(DT_tbody);
1535         }
1536         break;
1537     case Atom::TableRowLeft:
1538         if (!inTableHeader && !inTableBody) {
1539             inTableBody = true;
1540             writeStartTag(DT_tbody);
1541         }
1542         writeStartTag(DT_row);
1543         attr = atom->string();
1544         if (!attr.isEmpty()) {
1545             if (attr.contains('=')) {
1546                 int index = 0;
1547                 int from = 0;
1548                 QString values;
1549                 while (index >= 0) {
1550                     index = attr.indexOf('"',from);
1551                     if (index >= 0) {
1552                         ++index;
1553                         from = index;
1554                         index = attr.indexOf('"',from);
1555                         if (index > from) {
1556                             if (!values.isEmpty())
1557                                 values.append(' ');
1558                             values += attr.mid(from,index-from);
1559                             from = index+1;
1560                         }
1561                     }
1562                 }
1563                 attr = values;
1564             }
1565             xmlWriter().writeAttribute("outputclass", attr);
1566         }
1567         xmlWriter().writeAttribute("valign","top");
1568         break;
1569     case Atom::TableRowRight:
1570         writeEndTag(); // </row>
1571         break;
1572     case Atom::TableItemLeft:
1573         {
1574             QString values = "";
1575             writeStartTag(DT_entry);
1576             for (int i=0; i<atom->count(); ++i) {
1577                 attr = atom->string(i);
1578                 if (attr.contains('=')) {
1579                     int index = 0;
1580                     int from = 0;
1581                     while (index >= 0) {
1582                         index = attr.indexOf('"',from);
1583                         if (index >= 0) {
1584                             ++index;
1585                             from = index;
1586                             index = attr.indexOf('"',from);
1587                             if (index > from) {
1588                                 if (!values.isEmpty())
1589                                     values.append(' ');
1590                                 values += attr.mid(from,index-from);
1591                                 from = index+1;
1592                             }
1593                         }
1594                     }
1595                 }
1596                 else {
1597                     QStringList spans = attr.split(",");
1598                     if (spans.size() == 2) {
1599                         if ((spans[0].toInt()>1) || (spans[1].toInt()>1)) {
1600                             values += "span(" + spans[0] + "," + spans[1] + ")";
1601                         }
1602                     }
1603                 }
1604             }
1605             if (!values.isEmpty())
1606                 xmlWriter().writeAttribute("outputclass",values);
1607             if (matchAhead(atom, Atom::ParaLeft))
1608                 skipAhead = 1;
1609         }
1610         break;
1611     case Atom::TableItemRight:
1612         if (inTableHeader)
1613             writeEndTag(); // </entry>
1614         else {
1615             writeEndTag(); // </entry>
1616         }
1617         if (matchAhead(atom, Atom::ParaLeft))
1618             skipAhead = 1;
1619         break;
1620     case Atom::TableOfContents:
1621         {
1622             int numColumns = 1;
1623             const Node* node = relative;
1624 
1625             Doc::Sections sectionUnit = Doc::Section4;
1626             QStringList params = atom->string().split(",");
1627             QString columnText = params.at(0);
1628             QStringList pieces = columnText.split(" ", QString::SkipEmptyParts);
1629             if (pieces.size() >= 2) {
1630                 columnText = pieces.at(0);
1631                 pieces.pop_front();
1632                 QString path = pieces.join(" ").trimmed();
1633                 node = findNodeForTarget(path, relative, marker, atom);
1634             }
1635 
1636             if (params.size() == 2) {
1637                 numColumns = qMax(columnText.toInt(), numColumns);
1638                 sectionUnit = (Doc::Sections)params.at(1).toInt();
1639             }
1640 
1641             if (node)
1642                 generateTableOfContents(node,
1643                                         marker,
1644                                         sectionUnit,
1645                                         numColumns,
1646                                         relative);
1647         }
1648         break;
1649     case Atom::Target:
1650         if (in_para) {
1651             writeEndTag(); // </p>
1652             in_para = false;
1653         }
1654         writeStartTag(DT_p);
1655         writeGuidAttribute(Doc::canonicalTitle(atom->string()));
1656         xmlWriter().writeAttribute("outputclass","target");
1657         //xmlWriter().writeCharacters(protectEnc(atom->string()));
1658         writeEndTag(); // </p>
1659         break;
1660     case Atom::UnhandledFormat:
1661         writeStartTag(DT_b);
1662         xmlWriter().writeAttribute("outputclass","error");
1663         xmlWriter().writeCharacters("<Missing DITAXML>");
1664         writeEndTag(); // </b>
1665         break;
1666     case Atom::UnknownCommand:
1667         writeStartTag(DT_b);
1668         xmlWriter().writeAttribute("outputclass","error unknown-command");
1669         writeCharacters(protectEnc(atom->string()));
1670         writeEndTag(); // </b>
1671         break;
1672     case Atom::QmlText:
1673     case Atom::EndQmlText:
1674         // don't do anything with these. They are just tags.
1675         break;
1676     default:
1677         //        unknownAtom(atom);
1678         break;
1679     }
1680     return skipAhead;
1681 }
1682 
1683 /*!
1684   Generate a <cxxClass> element (and all the stuff inside it)
1685   for the C++ class represented by \a innerNode. \a marker is
1686   for marking up the code. I don't know what that means exactly.
1687  */
1688 void
generateClassLikeNode(const InnerNode * inner,CodeMarker * marker)1689 DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker)
1690 {
1691     QList<Section>::ConstIterator s;
1692 
1693     QString title;
1694     QString rawTitle;
1695     QString fullTitle;
1696     if (inner->type() == Node::Namespace) {
1697         const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner));
1698         rawTitle = marker->plainName(inner);
1699         fullTitle = marker->plainFullName(inner);
1700         title = rawTitle + " Namespace";
1701 
1702         /*
1703           Note: Because the C++ specialization we are using
1704           has no <cxxNamespace> element, we are using the
1705           <cxxClass> element with an outputclass attribute
1706           set to "namespace" .
1707          */
1708         generateHeader(inner, fullTitle);
1709         generateBrief(inner, marker); // <shortdesc>
1710         writeProlog(inner);
1711 
1712         writeStartTag(DT_cxxClassDetail);
1713         writeStartTag(DT_cxxClassDefinition);
1714         writeLocation(nsn);
1715         writeEndTag(); // <cxxClassDefinition>
1716 
1717         enterApiDesc(QString(),title);
1718         Text brief = nsn->doc().briefText(); // zzz
1719         if (!brief.isEmpty()) {
1720             writeStartTag(DT_p);
1721             generateText(brief, nsn, marker);
1722             writeEndTag(); // </p>
1723         }
1724         generateIncludes(nsn, marker);
1725         generateStatus(nsn, marker);
1726         generateThreadSafeness(nsn, marker);
1727         generateSince(nsn, marker);
1728 
1729         enterSection("h2","Detailed Description");
1730         generateBody(nsn, marker);
1731         generateAlsoList(nsn, marker);
1732         leaveSection();
1733         leaveSection(); // </apiDesc>
1734 
1735         bool needOtherSection = false;
1736         QList<Section> summarySections;
1737         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1738         if (!summarySections.isEmpty()) {
1739             enterSection("redundant",QString());
1740             s = summarySections.begin();
1741             while (s != summarySections.end()) {
1742                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1743                     if (!s->inherited.isEmpty())
1744                         needOtherSection = true;
1745                 }
1746                 else {
1747                     QString attr;
1748                     if (!s->members.isEmpty()) {
1749                         writeStartTag(DT_p);
1750                         attr  = cleanRef((*s).name).toLower() + " h2";
1751                         xmlWriter().writeAttribute("outputclass",attr);
1752                         writeCharacters(protectEnc((*s).name));
1753                         writeEndTag(); // </title>
1754                         generateSection(s->members, inner, marker, CodeMarker::Summary);
1755                         generateSectionInheritedList(*s, inner, marker);
1756                     }
1757                     if (!s->reimpMembers.isEmpty()) {
1758                         QString name = QString("Reimplemented ") + (*s).name;
1759                         attr = cleanRef(name).toLower() + " h2";
1760                         writeStartTag(DT_p);
1761                         xmlWriter().writeAttribute("outputclass",attr);
1762                         writeCharacters(protectEnc(name));
1763                         writeEndTag(); // </title>
1764                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1765                         generateSectionInheritedList(*s, inner, marker);
1766                     }
1767                 }
1768                 ++s;
1769             }
1770             if (needOtherSection) {
1771                 writeStartTag(DT_p);
1772                 xmlWriter().writeAttribute("outputclass","h3");
1773                 xmlWriter().writeCharacters("Additional Inherited Members");
1774                 writeEndTag(); // </title>
1775                 s = summarySections.begin();
1776                 while (s != summarySections.end()) {
1777                     if (s->members.isEmpty())
1778                         generateSectionInheritedList(*s, inner, marker);
1779                     ++s;
1780                 }
1781             }
1782             leaveSection();
1783         }
1784 
1785         writeEndTag(); // </cxxClassDetail>
1786 
1787         // not included: <related-links>
1788         // not included: <cxxClassNested>
1789 
1790         QList<Section> detailSections;
1791         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1792         s = detailSections.begin();
1793         while (s != detailSections.end()) {
1794             if ((*s).name == "Classes") {
1795                 writeNestedClasses((*s),nsn);
1796                 break;
1797             }
1798             ++s;
1799         }
1800 
1801         s = detailSections.begin();
1802         while (s != detailSections.end()) {
1803             if ((*s).name == "Function Documentation") {
1804                 writeFunctions((*s),nsn,marker);
1805             }
1806             else if ((*s).name == "Type Documentation") {
1807                 writeEnumerations((*s),marker);
1808                 writeTypedefs((*s),marker);
1809             }
1810             else if ((*s).name == "Namespaces") {
1811                 qDebug() << "Nested namespaces" << outFileName();
1812             }
1813             else if ((*s).name == "Macro Documentation") {
1814                 writeMacros((*s),marker);
1815             }
1816             ++s;
1817         }
1818 
1819         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1820         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1821         writeEndTag(); // </cxxClass>
1822     }
1823     else if (inner->type() == Node::Class) {
1824         const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner));
1825         rawTitle = marker->plainName(inner);
1826         fullTitle = marker->plainFullName(inner);
1827         title = rawTitle + " Class Reference";
1828 
1829         generateHeader(inner, fullTitle);
1830         generateBrief(inner, marker); // <shortdesc>
1831         writeProlog(inner);
1832 
1833         writeStartTag(DT_cxxClassDetail);
1834         writeStartTag(DT_cxxClassDefinition);
1835         writeStartTag(DT_cxxClassAccessSpecifier);
1836         xmlWriter().writeAttribute("value",inner->accessString());
1837         writeEndTag(); // <cxxClassAccessSpecifier>
1838         if (cn->isAbstract()) {
1839             writeStartTag(DT_cxxClassAbstract);
1840             xmlWriter().writeAttribute("name","abstract");
1841             xmlWriter().writeAttribute("value","abstract");
1842             writeEndTag(); // </cxxClassAbstract>
1843         }
1844         writeDerivations(cn, marker); // <cxxClassDerivations>
1845 
1846         // not included: <cxxClassTemplateParameters>
1847 
1848         writeLocation(cn);
1849         writeEndTag(); // <cxxClassDefinition>
1850 
1851         enterApiDesc(QString(),title);
1852         Text brief = cn->doc().briefText(); // zzz
1853         if (!brief.isEmpty()) {
1854             writeStartTag(DT_p);
1855             generateText(brief, cn, marker);
1856             writeEndTag(); // </p>
1857         }
1858         generateIncludes(cn, marker);
1859         generateStatus(cn, marker);
1860         generateInherits(cn, marker);
1861         generateInheritedBy(cn, marker);
1862         generateThreadSafeness(cn, marker);
1863         generateSince(cn, marker);
1864         enterSection("h2","Detailed Description");
1865         generateBody(cn, marker);
1866         generateAlsoList(cn, marker);
1867         leaveSection();
1868         leaveSection(); // </apiDesc>
1869 
1870         bool needOtherSection = false;
1871         QList<Section> summarySections;
1872         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1873         if (!summarySections.isEmpty()) {
1874             enterSection("redundant",QString());
1875             s = summarySections.begin();
1876             while (s != summarySections.end()) {
1877                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1878                     if (!s->inherited.isEmpty())
1879                         needOtherSection = true;
1880                 }
1881                 else {
1882                     QString attr;
1883                     if (!s->members.isEmpty()) {
1884                         writeStartTag(DT_p);
1885                         attr = cleanRef((*s).name).toLower() + " h2";
1886                         xmlWriter().writeAttribute("outputclass",attr);
1887                         writeCharacters(protectEnc((*s).name));
1888                         writeEndTag(); // </p>
1889                         generateSection(s->members, inner, marker, CodeMarker::Summary);
1890                         generateSectionInheritedList(*s, inner, marker);
1891                     }
1892                     if (!s->reimpMembers.isEmpty()) {
1893                         QString name = QString("Reimplemented ") + (*s).name;
1894                         attr = cleanRef(name).toLower() + " h2";
1895                         writeStartTag(DT_p);
1896                         xmlWriter().writeAttribute("outputclass",attr);
1897                         writeCharacters(protectEnc(name));
1898                         writeEndTag(); // </p>
1899                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
1900                         generateSectionInheritedList(*s, inner, marker);
1901                     }
1902                 }
1903                 ++s;
1904             }
1905             if (needOtherSection) {
1906                 writeStartTag(DT_p);
1907                 xmlWriter().writeAttribute("outputclass","h3");
1908                 xmlWriter().writeCharacters("Additional Inherited Members");
1909                 writeEndTag(); // </p>
1910                 s = summarySections.begin();
1911                 while (s != summarySections.end()) {
1912                     if (s->members.isEmpty())
1913                         generateSectionInheritedList(*s, inner, marker);
1914                     ++s;
1915                 }
1916             }
1917             leaveSection();
1918         }
1919 
1920         // not included: <example> or <apiImpl>
1921 
1922         writeEndTag(); // </cxxClassDetail>
1923 
1924         // not included: <related-links>
1925         // not included: <cxxClassNested>
1926 
1927         QList<Section> detailSections;
1928         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
1929         s = detailSections.begin();
1930         while (s != detailSections.end()) {
1931             if ((*s).name == "Member Function Documentation") {
1932                 writeFunctions((*s),cn,marker);
1933             }
1934             else if ((*s).name == "Member Type Documentation") {
1935                 writeEnumerations((*s),marker);
1936                 writeTypedefs((*s),marker);
1937             }
1938             else if ((*s).name == "Member Variable Documentation") {
1939                 writeDataMembers((*s),marker);
1940             }
1941             else if ((*s).name == "Property Documentation") {
1942                 writeProperties((*s),marker);
1943             }
1944             else if ((*s).name == "Macro Documentation") {
1945                 writeMacros((*s),marker);
1946             }
1947             ++s;
1948         }
1949 
1950         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
1951         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
1952         writeEndTag(); // </cxxClass>
1953     }
1954     else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) {
1955         const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner));
1956         rawTitle = marker->plainName(inner);
1957         fullTitle = marker->plainFullName(inner);
1958         title = rawTitle;
1959 
1960         /*
1961           Note: Because the C++ specialization we are using
1962           has no <cxxHeaderFile> element, we are using the
1963           <cxxClass> element with an outputclass attribute
1964           set to "headerfile" .
1965          */
1966         generateHeader(inner, fullTitle);
1967         generateBrief(inner, marker); // <shortdesc>
1968         writeProlog(inner);
1969 
1970         writeStartTag(DT_cxxClassDetail);
1971         enterApiDesc(QString(),title);
1972         Text brief = fn->doc().briefText(); // zzz
1973         if (!brief.isEmpty()) {
1974             writeStartTag(DT_p);
1975             generateText(brief, fn, marker);
1976             writeEndTag(); // </p>
1977         }
1978         generateIncludes(fn, marker);
1979         generateStatus(fn, marker);
1980         generateThreadSafeness(fn, marker);
1981         generateSince(fn, marker);
1982         generateSince(fn, marker);
1983         enterSection("h2","Detailed Description");
1984         generateBody(fn, marker);
1985         generateAlsoList(fn, marker);
1986         leaveSection();
1987         leaveSection(); // </apiDesc>
1988 
1989         bool needOtherSection = false;
1990         QList<Section> summarySections;
1991         summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
1992         if (!summarySections.isEmpty()) {
1993             enterSection("redundant",QString());
1994             s = summarySections.begin();
1995             while (s != summarySections.end()) {
1996                 if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
1997                     if (!s->inherited.isEmpty())
1998                         needOtherSection = true;
1999                 }
2000                 else {
2001                     QString attr;
2002                     if (!s->members.isEmpty()) {
2003                         writeStartTag(DT_p);
2004                         attr = cleanRef((*s).name).toLower() + " h2";
2005                         xmlWriter().writeAttribute("outputclass",attr);
2006                         writeCharacters(protectEnc((*s).name));
2007                         writeEndTag(); // </p>
2008                         generateSection(s->members, inner, marker, CodeMarker::Summary);
2009                         generateSectionInheritedList(*s, inner, marker);
2010                     }
2011                     if (!s->reimpMembers.isEmpty()) {
2012                         QString name = QString("Reimplemented ") + (*s).name;
2013                         attr = cleanRef(name).toLower() + " h2";
2014                         writeStartTag(DT_p);
2015                         xmlWriter().writeAttribute("outputclass",attr);
2016                         writeCharacters(protectEnc(name));
2017                         writeEndTag(); // </p>
2018                         generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
2019                         generateSectionInheritedList(*s, inner, marker);
2020                     }
2021                 }
2022                 ++s;
2023             }
2024             if (needOtherSection) {
2025                 enterSection("additional-inherited-members redundant",QString());
2026                 writeStartTag(DT_p);
2027                 xmlWriter().writeAttribute("outputclass","h3");
2028                 xmlWriter().writeCharacters("Additional Inherited Members");
2029                 writeEndTag(); // </p>
2030                 s = summarySections.begin();
2031                 while (s != summarySections.end()) {
2032                     if (s->members.isEmpty())
2033                         generateSectionInheritedList(*s, inner, marker);
2034                     ++s;
2035                 }
2036             }
2037             leaveSection();
2038         }
2039 
2040         writeEndTag(); // </cxxClassDetail>
2041 
2042         // not included: <related-links>
2043         // not included: <cxxClassNested>
2044 
2045         QList<Section> detailSections;
2046         detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
2047         s = detailSections.begin();
2048         while (s != detailSections.end()) {
2049             if ((*s).name == "Classes") {
2050                 writeNestedClasses((*s),fn);
2051                 break;
2052             }
2053             ++s;
2054         }
2055 
2056         s = detailSections.begin();
2057         while (s != detailSections.end()) {
2058             if ((*s).name == "Function Documentation") {
2059                 writeFunctions((*s),fn,marker);
2060             }
2061             else if ((*s).name == "Type Documentation") {
2062                 writeEnumerations((*s),marker);
2063                 writeTypedefs((*s),marker);
2064             }
2065             else if ((*s).name == "Namespaces") {
2066                 qDebug() << "Nested namespaces" << outFileName();
2067             }
2068             else if ((*s).name == "Macro Documentation") {
2069                 writeMacros((*s),marker);
2070             }
2071             ++s;
2072         }
2073         generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
2074         generateLowStatusMembers(inner,marker,CodeMarker::Compat);
2075         writeEndTag(); // </cxxClass>
2076     }
2077     else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
2078         const QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(inner));
2079         const ClassNode* cn = qcn->classNode();
2080         rawTitle = marker->plainName(inner);
2081         fullTitle = marker->plainFullName(inner);
2082         title = rawTitle + " Element Reference";
2083         //QString fullTitle = fake->fullTitle();
2084         //QString htmlTitle = fullTitle;
2085 
2086         generateHeader(inner, fullTitle);
2087         generateBrief(inner, marker); // <shortdesc>
2088         writeProlog(inner);
2089 
2090         writeStartTag(DT_cxxClassDetail);
2091         enterApiDesc(QString(),title);
2092         Text brief = qcn->doc().briefText(); // zzz
2093         if (!brief.isEmpty()) {
2094             writeStartTag(DT_p);
2095             generateText(brief, qcn, marker);
2096             writeEndTag(); // </p>
2097         }
2098         generateQmlInstantiates(qcn, marker);
2099         generateQmlInherits(qcn, marker);
2100         generateQmlInheritedBy(qcn, marker);
2101         generateSince(qcn, marker);
2102         enterSection("h2","Detailed Description");
2103         generateBody(qcn, marker);
2104         if (cn) {
2105             generateQmlText(cn->doc().body(), cn, marker, qcn->name());
2106             generateAlsoList(cn, marker);
2107         }
2108         leaveSection();
2109         leaveSection(); // </apiDesc>
2110 
2111         QList<Section> summarySections;
2112         summarySections = marker->qmlSections(qcn,CodeMarker::Summary,0);
2113         if (!summarySections.isEmpty()) {
2114             enterSection("redundant",QString());
2115             s = summarySections.begin();
2116             while (s != summarySections.end()) {
2117                 QString attr;
2118                 if (!s->members.isEmpty()) {
2119                     writeStartTag(DT_p);
2120                     attr = cleanRef((*s).name).toLower() + " h2";
2121                     xmlWriter().writeAttribute("outputclass",attr);
2122                     writeCharacters(protectEnc((*s).name));
2123                     writeEndTag(); // </p>
2124                     generateQmlSummary(*s,qcn,marker);
2125                     //generateSection(s->members, inner, marker, CodeMarker::Summary);
2126                     //generateSectionInheritedList(*s, inner, marker);
2127                 }
2128                 ++s;
2129             }
2130             leaveSection();
2131         }
2132 
2133         QList<Section> detailSections;
2134         detailSections = marker->qmlSections(qcn,CodeMarker::Detailed,0);
2135         if (!detailSections.isEmpty()) {
2136             enterSection("details",QString());
2137             s = detailSections.begin();
2138             while (s != detailSections.end()) {
2139                 if (!s->members.isEmpty()) {
2140                     QString attr;
2141                     writeStartTag(DT_p);
2142                     attr = cleanRef((*s).name).toLower() + " h2";
2143                     xmlWriter().writeAttribute("outputclass",attr);
2144                     writeCharacters(protectEnc((*s).name));
2145                     writeEndTag(); // </p>
2146                     NodeList::ConstIterator m = (*s).members.begin();
2147                     while (m != (*s).members.end()) {
2148                         generateDetailedQmlMember(*m, qcn, marker);
2149                         ++m;
2150                     }
2151                 }
2152                 ++s;
2153             }
2154             leaveSection();
2155         }
2156         writeEndTag(); // </cxxClassDetail>
2157         writeEndTag(); // </cxxClass>
2158     }
2159 }
2160 
2161 
2162 /*!
2163   Write a list item for a \a link with the given \a text.
2164  */
writeXrefListItem(const QString & link,const QString & text)2165 void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
2166 {
2167     writeStartTag(DT_li);
2168     writeStartTag(DT_xref);
2169     // formathtml
2170     xmlWriter().writeAttribute("href",link);
2171     writeCharacters(text);
2172     writeEndTag(); // </xref>
2173     writeEndTag(); // </li>
2174 }
2175 
2176 /*!
2177   Generate the html page for a qdoc file that doesn't map
2178   to an underlying c++ file.
2179  */
generateFakeNode(const FakeNode * fake,CodeMarker * marker)2180 void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker)
2181 {
2182     QList<Section> sections;
2183     QList<Section>::const_iterator s;
2184     QString fullTitle = fake->fullTitle();
2185 
2186     if (fake->subType() == Node::QmlBasicType) {
2187         fullTitle = "QML Basic Type: " + fullTitle;
2188     }
2189 
2190     generateHeader(fake, fullTitle);
2191     generateBrief(fake, marker); // <shortdesc>
2192     writeProlog(fake);
2193 
2194     writeStartTag(DT_body);
2195     enterSection(QString(),QString());
2196     if (fake->subType() == Node::Module) {
2197         generateStatus(fake, marker);
2198         if (moduleNamespaceMap.contains(fake->name())) {
2199             enterSection("h2","Namespaces");
2200             generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
2201             leaveSection();
2202         }
2203         if (moduleClassMap.contains(fake->name())) {
2204             enterSection("h2","Classes");
2205             generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
2206             leaveSection();
2207         }
2208     }
2209 
2210     if (fake->doc().isEmpty()) {
2211         if (fake->subType() == Node::File) {
2212             Text text;
2213             Quoter quoter;
2214             writeStartTag(DT_p);
2215             xmlWriter().writeAttribute("outputclass", "small-subtitle");
2216             text << fake->subTitle();
2217             generateText(text, fake, marker);
2218             writeEndTag(); // </p>
2219             Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
2220             QString code = quoter.quoteTo(fake->location(), "", "");
2221             text.clear();
2222             text << Atom(Atom::Code, code);
2223             generateText(text, fake, marker);
2224         }
2225     }
2226     else {
2227         if (fake->subType() == Node::Module) {
2228          enterSection("h2","Detailed Description");
2229          generateBody(fake, marker);
2230          leaveSection();
2231         }
2232         else
2233             generateBody(fake, marker);
2234         generateAlsoList(fake, marker);
2235 
2236         if (!fake->groupMembers().isEmpty()) {
2237             NodeMap groupMembersMap;
2238             foreach (const Node *node, fake->groupMembers()) {
2239                 if (node->type() == Node::Class || node->type() == Node::Namespace)
2240                     groupMembersMap[node->name()] = node;
2241             }
2242             generateAnnotatedList(fake, marker, groupMembersMap);
2243         }
2244     }
2245     leaveSection(); // </section>
2246     writeEndTag(); // </body>
2247     writeRelatedLinks(fake, marker);
2248     writeEndTag(); // </topic>
2249 }
2250 
2251 /*!
2252   This function writes a \e{<link>} element inside a
2253   \e{<related-links>} element.
2254 
2255   \sa writeRelatedLinks()
2256  */
writeLink(const Node * node,const QString & text,const QString & role)2257 void DitaXmlGenerator::writeLink(const Node* node,
2258                                  const QString& text,
2259                                  const QString& role)
2260 {
2261     if (node) {
2262         QString link = fileName(node) + "#" + node->guid();
2263         writeStartTag(DT_link);
2264         xmlWriter().writeAttribute("href", link);
2265         xmlWriter().writeAttribute("role", role);
2266         writeStartTag(DT_linktext);
2267         writeCharacters(text);
2268         writeEndTag(); // </linktext>
2269         writeEndTag(); // </link>
2270     }
2271 }
2272 
2273 /*!
2274   This function writes a \e{<related-links>} element, which
2275   contains the \c{next}, \c{previous}, and \c{start}
2276   links for topic pages that have them. Note that the
2277   value of the \e role attribute is \c{parent} for the
2278   \c{start} link.
2279  */
writeRelatedLinks(const FakeNode * node,CodeMarker * marker)2280 void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker)
2281 {
2282     const Node* linkNode = 0;
2283     QPair<QString,QString> linkPair;
2284     if (node && !node->links().empty()) {
2285         writeStartTag(DT_relatedLinks);
2286         if (node->links().contains(Node::PreviousLink)) {
2287             linkPair = node->links()[Node::PreviousLink];
2288             linkNode = findNodeForTarget(linkPair.first, node, marker);
2289             writeLink(linkNode, linkPair.second, "previous");
2290         }
2291         if (node->links().contains(Node::NextLink)) {
2292             linkPair = node->links()[Node::NextLink];
2293             linkNode = findNodeForTarget(linkPair.first, node, marker);
2294             writeLink(linkNode, linkPair.second, "next");
2295         }
2296         if (node->links().contains(Node::StartLink)) {
2297             linkPair = node->links()[Node::StartLink];
2298             linkNode = findNodeForTarget(linkPair.first, node, marker);
2299             writeLink(linkNode, linkPair.second, "parent");
2300         }
2301         writeEndTag(); // </related-links>
2302     }
2303 }
2304 
2305 /*!
2306   Returns "xml" for this subclass of class Generator.
2307  */
fileExtension(const Node *) const2308 QString DitaXmlGenerator::fileExtension(const Node * /* node */) const
2309 {
2310     return "xml";
2311 }
2312 
2313 /*!
2314   Writes an XML file header to the current XML stream. This
2315   depends on which kind of DITA XML file is being generated,
2316   which is determined by the \a node type and subtype and the
2317   \a subpage flag. If the \subpage flag is true, a \c{<topic>}
2318   header is written, regardless of the type of \a node.
2319  */
generateHeader(const Node * node,const QString & name,bool subpage)2320 void DitaXmlGenerator::generateHeader(const Node* node,
2321                                       const QString& name,
2322                                       bool subpage)
2323 {
2324     if (!node)
2325         return;
2326 
2327     DitaTag mainTag = DT_cxxClass;
2328     DitaTag nameTag = DT_apiName;
2329     QString doctype;
2330     QString dtd;
2331     QString base;
2332     QString version;
2333     QString outputclass;
2334 
2335     if (node->type() == Node::Class) {
2336         mainTag = DT_cxxClass;
2337         nameTag = DT_apiName;
2338         dtd = "dtd/cxxClass.dtd";
2339         version = "0.6.0";
2340         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2341             " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2342             version + "//EN\" \"" + dtd + "\">";
2343     }
2344     else if (node->type() == Node::Namespace) {
2345         mainTag = DT_cxxClass;
2346         nameTag = DT_apiName;
2347         dtd = "dtd/cxxClass.dtd";
2348         version = "0.6.0";
2349         doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2350             " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2351             version + "//EN\" \"" + dtd + "\">";
2352         outputclass = "namespace";
2353     }
2354     else if (node->type() == Node::Fake || subpage) {
2355         if (node->subType() == Node::HeaderFile) {
2356             mainTag = DT_cxxClass;
2357             nameTag = DT_apiName;
2358             dtd = "dtd/cxxClass.dtd";
2359             version = "0.6.0";
2360             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2361                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2362                 version + "//EN\" \"" + dtd + "\">";
2363             outputclass = "headerfile";
2364         }
2365         else if (node->subType() == Node::QmlClass) {
2366             mainTag = DT_cxxClass;
2367             nameTag = DT_apiName;
2368             dtd = "dtd/cxxClass.dtd";
2369             version = "0.6.0";
2370             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2371                 " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" +
2372                 version + "//EN\" \"" + dtd + "\">";
2373             outputclass = "QML-class";
2374         }
2375         else {
2376             mainTag = DT_topic;
2377             nameTag = DT_title;
2378             dtd = "dtd/topic.dtd";
2379             doctype = "<!DOCTYPE " + ditaTags[mainTag] +
2380                 " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">";
2381             switch (node->subType()) {
2382             case Node::Page:
2383                 outputclass = "page";
2384                 break;
2385             case Node::Group:
2386                 outputclass = "group";
2387                 break;
2388             case Node::Example:
2389                 outputclass = "example";
2390                 break;
2391             case Node::File:
2392                 outputclass = "file";
2393                 break;
2394             case Node::Image:  // not used
2395                 outputclass = "image";
2396                 break;
2397             case Node::Module:
2398                 outputclass = "module";
2399                 break;
2400             case Node::ExternalPage: // not used
2401                 outputclass = "externalpage";
2402                 break;
2403             default:
2404                 outputclass = "page";
2405             }
2406         }
2407     }
2408 
2409     xmlWriter().writeDTD(doctype);
2410     xmlWriter().writeComment(node->doc().location().fileName());
2411     writeStartTag(mainTag);
2412     xmlWriter().writeAttribute("id",node->guid());
2413     if (!outputclass.isEmpty())
2414         xmlWriter().writeAttribute("outputclass",outputclass);
2415     writeStartTag(nameTag); // <title> or <apiName>
2416     writeCharacters(name);
2417     writeEndTag(); // </title> or </apiName>
2418 }
2419 
2420 /*!
2421   Outputs the \e brief command as a <shortdesc> element.
2422  */
generateBrief(const Node * node,CodeMarker * marker)2423 void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker)
2424 {
2425     Text brief = node->doc().briefText(true); // zzz
2426     if (!brief.isEmpty()) {
2427         generateText(brief, node, marker);
2428     }
2429 }
2430 
2431 /*!
2432   Writes the \c {#include ...} required to include the class
2433   or namespace in a compilation.
2434  */
generateIncludes(const InnerNode * inner,CodeMarker * marker)2435 void DitaXmlGenerator::generateIncludes(const InnerNode* inner, CodeMarker* marker)
2436 {
2437     if (!inner->includes().isEmpty()) {
2438         writeStartTag(DT_codeblock);
2439         writeText(marker->markedUpIncludes(inner->includes()), marker, inner);
2440         writeEndTag(); // </codeblock>
2441     }
2442 }
2443 
2444 /*!
2445   zzz
2446   Generates a table of contents beginning at \a node.
2447   Currently just returns without writing anything.
2448  */
generateTableOfContents(const Node * node,CodeMarker * marker,Doc::Sections sectionUnit,int numColumns,const Node * relative)2449 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2450                                                CodeMarker* marker,
2451                                                Doc::Sections sectionUnit,
2452                                                int numColumns,
2453                                                const Node* relative)
2454 
2455 {
2456     return;
2457     if (!node->doc().hasTableOfContents())
2458         return;
2459     QList<Atom *> toc = node->doc().tableOfContents();
2460     if (toc.isEmpty())
2461         return;
2462 
2463     QString nodeName = "";
2464     if (node != relative)
2465         nodeName = node->name();
2466 
2467     QStringList sectionNumber;
2468     int columnSize = 0;
2469 
2470     QString tdTag;
2471     if (numColumns > 1) {
2472         tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/
2473         out() << "<table class=\"toc\">\n<tr class=\"topAlign\">"
2474               << tdTag << "\n";
2475     }
2476 
2477     // disable nested links in table of contents
2478     inContents = true;
2479     inLink = true;
2480 
2481     for (int i = 0; i < toc.size(); ++i) {
2482         Atom *atom = toc.at(i);
2483 
2484         int nextLevel = atom->string().toInt();
2485         if (nextLevel > (int)sectionUnit)
2486             continue;
2487 
2488         if (sectionNumber.size() < nextLevel) {
2489             do {
2490                 out() << "<ul>";
2491                 sectionNumber.append("1");
2492             } while (sectionNumber.size() < nextLevel);
2493         }
2494         else {
2495             while (sectionNumber.size() > nextLevel) {
2496                 out() << "</ul>\n";
2497                 sectionNumber.removeLast();
2498             }
2499             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2500         }
2501         int numAtoms;
2502         Text headingText = Text::sectionHeading(atom);
2503 
2504         if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) {
2505             out() << "</ul></td>" << tdTag << "<ul>\n";
2506             columnSize = 0;
2507         }
2508         out() << "<li>";
2509         out() << "<xref href=\""
2510               << nodeName
2511               << "#"
2512               << Doc::canonicalTitle(headingText.toString())
2513               << "\">";
2514         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2515         out() << "</xref></li>\n";
2516 
2517         ++columnSize;
2518     }
2519     while (!sectionNumber.isEmpty()) {
2520         out() << "</ul>\n";
2521         sectionNumber.removeLast();
2522     }
2523 
2524     if (numColumns > 1)
2525         out() << "</td></tr></table>\n";
2526 
2527     inContents = false;
2528     inLink = false;
2529 }
2530 
2531 /*!
2532   zzz
2533   Revised for the new doc format.
2534   Generates a table of contents beginning at \a node.
2535  */
generateTableOfContents(const Node * node,CodeMarker * marker,QList<Section> * sections)2536 void DitaXmlGenerator::generateTableOfContents(const Node* node,
2537                                                CodeMarker* marker,
2538                                                QList<Section>* sections)
2539 {
2540     QList<Atom*> toc;
2541     if (node->doc().hasTableOfContents())
2542         toc = node->doc().tableOfContents();
2543     if (toc.isEmpty() && !sections && (node->subType() != Node::Module))
2544         return;
2545 
2546     QStringList sectionNumber;
2547     int detailsBase = 0;
2548 
2549     // disable nested links in table of contents
2550     inContents = true;
2551     inLink = true;
2552 
2553     out() << "<div class=\"toc\">\n";
2554     out() << "<h3>Contents</h3>\n";
2555     sectionNumber.append("1");
2556     out() << "<ul>\n";
2557 
2558     if (node->subType() == Node::Module) {
2559         if (moduleNamespaceMap.contains(node->name())) {
2560             out() << "<li class=\"level"
2561                   << sectionNumber.size()
2562                   << "\"><xref href=\"#"
2563                   << registerRef("namespaces")
2564                   << "\">Namespaces</xref></li>\n";
2565         }
2566         if (moduleClassMap.contains(node->name())) {
2567             out() << "<li class=\"level"
2568                   << sectionNumber.size()
2569                   << "\"><xref href=\"#"
2570                   << registerRef("classes")
2571                   << "\">Classes</xref></li>\n";
2572         }
2573         out() << "<li class=\"level"
2574               << sectionNumber.size()
2575               << "\"><xref href=\"#"
2576               << registerRef("details")
2577               << "\">Detailed Description</xref></li>\n";
2578         for (int i = 0; i < toc.size(); ++i) {
2579             if (toc.at(i)->string().toInt() == 1) {
2580                 detailsBase = 1;
2581                 break;
2582             }
2583         }
2584     }
2585     else if (sections && (node->type() == Node::Class)) {
2586         QList<Section>::ConstIterator s = sections->begin();
2587         while (s != sections->end()) {
2588             if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) {
2589                 out() << "<li class=\"level"
2590                       << sectionNumber.size()
2591                       << "\"><xref href=\"#"
2592                       << registerRef((*s).pluralMember)
2593                       << "\">" << (*s).name
2594                       << "</xref></li>\n";
2595             }
2596             ++s;
2597         }
2598         out() << "<li class=\"level"
2599               << sectionNumber.size()
2600               << "\"><xref href=\"#"
2601               << registerRef("details")
2602               << "\">Detailed Description</xref></li>\n";
2603         for (int i = 0; i < toc.size(); ++i) {
2604             if (toc.at(i)->string().toInt() == 1) {
2605                 detailsBase = 1;
2606                 break;
2607             }
2608         }
2609     }
2610 
2611     for (int i = 0; i < toc.size(); ++i) {
2612         Atom *atom = toc.at(i);
2613         int nextLevel = atom->string().toInt() + detailsBase;
2614         if (sectionNumber.size() < nextLevel) {
2615             do {
2616                 sectionNumber.append("1");
2617             } while (sectionNumber.size() < nextLevel);
2618         }
2619         else {
2620             while (sectionNumber.size() > nextLevel) {
2621                 sectionNumber.removeLast();
2622             }
2623             sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
2624         }
2625         int numAtoms;
2626         Text headingText = Text::sectionHeading(atom);
2627         QString s = headingText.toString();
2628         out() << "<li class=\"level"
2629               << sectionNumber.size()
2630               << "\">";
2631         out() << "<xref href=\""
2632               << "#"
2633               << Doc::canonicalTitle(s)
2634               << "\">";
2635         generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
2636         out() << "</xref></li>\n";
2637     }
2638     while (!sectionNumber.isEmpty()) {
2639         sectionNumber.removeLast();
2640     }
2641     out() << "</ul>\n";
2642     out() << "</div>\n";
2643     inContents = false;
2644     inLink = false;
2645 }
2646 
generateLowStatusMembers(const InnerNode * inner,CodeMarker * marker,CodeMarker::Status status)2647 void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner,
2648                                                 CodeMarker* marker,
2649                                                 CodeMarker::Status status)
2650 {
2651     QString attribute;
2652     if (status == CodeMarker::Compat)
2653         attribute = "Qt3-support";
2654     else if (status == CodeMarker::Obsolete)
2655         attribute = "obsolete";
2656     else
2657         return;
2658 
2659     QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status);
2660     QMutableListIterator<Section> j(sections);
2661     while (j.hasNext()) {
2662         if (j.next().members.size() == 0)
2663             j.remove();
2664     }
2665     if (sections.isEmpty())
2666         return;
2667 
2668     QList<Section>::ConstIterator s = sections.begin();
2669     while (s != sections.end()) {
2670         if ((*s).name == "Member Function Documentation") {
2671             writeFunctions((*s),inner,marker,attribute);
2672         }
2673         else if ((*s).name == "Member Type Documentation") {
2674             writeEnumerations((*s),marker,attribute);
2675             writeTypedefs((*s),marker,attribute);
2676         }
2677         else if ((*s).name == "Member Variable Documentation") {
2678             writeDataMembers((*s),marker,attribute);
2679         }
2680         else if ((*s).name == "Property Documentation") {
2681             writeProperties((*s),marker,attribute);
2682         }
2683         else if ((*s).name == "Macro Documentation") {
2684             writeMacros((*s),marker,attribute);
2685         }
2686         ++s;
2687     }
2688 }
2689 
2690 /*!
2691   Write the XML for the class hierarchy to the current XML stream.
2692  */
generateClassHierarchy(const Node * relative,CodeMarker * marker,const QMap<QString,const Node * > & classMap)2693 void DitaXmlGenerator::generateClassHierarchy(const Node* relative,
2694                                               CodeMarker* marker,
2695                                               const QMap<QString,const Node*>& classMap)
2696 {
2697     if (classMap.isEmpty())
2698         return;
2699 
2700     NodeMap topLevel;
2701     NodeMap::ConstIterator c = classMap.begin();
2702     while (c != classMap.end()) {
2703         const ClassNode* classe = static_cast<const ClassNode*>(*c);
2704         if (classe->baseClasses().isEmpty())
2705             topLevel.insert(classe->name(), classe);
2706         ++c;
2707     }
2708 
2709     QStack<NodeMap > stack;
2710     stack.push(topLevel);
2711 
2712     writeStartTag(DT_ul);
2713     while (!stack.isEmpty()) {
2714         if (stack.top().isEmpty()) {
2715             stack.pop();
2716             writeEndTag(); // </ul>
2717             if (!stack.isEmpty())
2718                 writeEndTag(); // </li>
2719         }
2720         else {
2721             const ClassNode *child =
2722                 static_cast<const ClassNode *>(*stack.top().begin());
2723             writeStartTag(DT_li);
2724             generateFullName(child, relative, marker);
2725             writeEndTag(); // </li>
2726             stack.top().erase(stack.top().begin());
2727 
2728             NodeMap newTop;
2729             foreach (const RelatedClass &d, child->derivedClasses()) {
2730                 if (d.access != Node::Private && !d.node->doc().isEmpty())
2731                     newTop.insert(d.node->name(), d.node);
2732             }
2733             if (!newTop.isEmpty()) {
2734                 stack.push(newTop);
2735                 writeStartTag(DT_li);
2736                 writeStartTag(DT_ul);
2737             }
2738         }
2739     }
2740 }
2741 
2742 /*!
2743   Write XML for the contents of the \a nodeMap to the current
2744   XML stream.
2745  */
generateAnnotatedList(const Node * relative,CodeMarker * marker,const NodeMap & nodeMap)2746 void DitaXmlGenerator::generateAnnotatedList(const Node* relative,
2747                                              CodeMarker* marker,
2748                                              const NodeMap& nodeMap)
2749 {
2750     if (nodeMap.isEmpty())
2751         return;
2752     writeStartTag(DT_table);
2753     xmlWriter().writeAttribute("outputclass","annotated");
2754     writeStartTag(DT_tgroup);
2755     xmlWriter().writeAttribute("cols","2");
2756     writeStartTag(DT_tbody);
2757 
2758     foreach (const QString& name, nodeMap.keys()) {
2759         const Node* node = nodeMap[name];
2760 
2761         if (node->status() == Node::Obsolete)
2762             continue;
2763 
2764         writeStartTag(DT_row);
2765         writeStartTag(DT_entry);
2766         writeStartTag(DT_p);
2767         generateFullName(node, relative, marker);
2768         writeEndTag(); // </p>
2769         writeEndTag(); // <entry>
2770 
2771         if (!(node->type() == Node::Fake)) {
2772             Text brief = node->doc().trimmedBriefText(name);
2773             if (!brief.isEmpty()) {
2774                 writeStartTag(DT_entry);
2775                 writeStartTag(DT_p);
2776                 generateText(brief, node, marker);
2777                 writeEndTag(); // </p>
2778                 writeEndTag(); // <entry>
2779             }
2780         }
2781         else {
2782             writeStartTag(DT_entry);
2783             writeStartTag(DT_p);
2784             writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz
2785             writeEndTag(); // </p>
2786             writeEndTag(); // <entry>
2787         }
2788         writeEndTag(); // </row>
2789     }
2790     writeEndTag(); // </tbody>
2791     writeEndTag(); // </tgroup>
2792     writeEndTag(); // </table>
2793 }
2794 
2795 /*!
2796   This function finds the common prefix of the names of all
2797   the classes in \a classMap and then generates a compact
2798   list of the class names alphabetized on the part of the
2799   name not including the common prefix. You can tell the
2800   function to use \a comonPrefix as the common prefix, but
2801   normally you let it figure it out itself by looking at
2802   the name of the first and last classes in \a classMap.
2803  */
generateCompactList(const Node * relative,CodeMarker * marker,const NodeMap & classMap,bool includeAlphabet,QString commonPrefix)2804 void DitaXmlGenerator::generateCompactList(const Node* relative,
2805                                            CodeMarker* marker,
2806                                            const NodeMap& classMap,
2807                                            bool includeAlphabet,
2808                                            QString commonPrefix)
2809 {
2810     const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
2811 
2812     if (classMap.isEmpty())
2813         return;
2814 
2815     /*
2816       If commonPrefix is not empty, then the caller knows what
2817       the common prefix is and has passed it in, so just use that
2818       one.
2819      */
2820     int commonPrefixLen = commonPrefix.length();
2821     if (commonPrefixLen == 0) {
2822         QString first;
2823         QString last;
2824 
2825         /*
2826           The caller didn't pass in a common prefix, so get the common
2827           prefix by looking at the class names of the first and last
2828           classes in the class map. Discard any namespace names and
2829           just use the bare class names. For Qt, the prefix is "Q".
2830 
2831           Note that the algorithm used here to derive the common prefix
2832           from the first and last classes in alphabetical order (QAccel
2833           and QXtWidget in Qt 2.1), fails if either class name does not
2834           begin with Q.
2835         */
2836 
2837         NodeMap::const_iterator iter = classMap.begin();
2838         while (iter != classMap.end()) {
2839             if (!iter.key().contains("::")) {
2840                 first = iter.key();
2841                 break;
2842             }
2843             ++iter;
2844         }
2845 
2846         if (first.isEmpty())
2847             first = classMap.begin().key();
2848 
2849         iter = classMap.end();
2850         while (iter != classMap.begin()) {
2851             --iter;
2852             if (!iter.key().contains("::")) {
2853                 last = iter.key();
2854                 break;
2855             }
2856         }
2857 
2858         if (last.isEmpty())
2859             last = classMap.begin().key();
2860 
2861         if (classMap.size() > 1) {
2862             while (commonPrefixLen < first.length() + 1 &&
2863                    commonPrefixLen < last.length() + 1 &&
2864                    first[commonPrefixLen] == last[commonPrefixLen])
2865                 ++commonPrefixLen;
2866         }
2867 
2868         commonPrefix = first.left(commonPrefixLen);
2869     }
2870 
2871     /*
2872       Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
2873       underscore (_). QAccel will fall in paragraph 10 (A) and
2874       QXtWidget in paragraph 33 (X). This is the only place where we
2875       assume that NumParagraphs is 37. Each paragraph is a NodeMap.
2876     */
2877     NodeMap paragraph[NumParagraphs+1];
2878     QString paragraphName[NumParagraphs+1];
2879     QSet<char> usedParagraphNames;
2880 
2881     NodeMap::ConstIterator c = classMap.begin();
2882     while (c != classMap.end()) {
2883         QStringList pieces = c.key().split("::");
2884         QString key;
2885         int idx = commonPrefixLen;
2886         if (!pieces.last().startsWith(commonPrefix))
2887             idx = 0;
2888         if (pieces.size() == 1)
2889             key = pieces.last().mid(idx).toLower();
2890         else
2891             key = pieces.last().toLower();
2892 
2893         int paragraphNr = NumParagraphs - 1;
2894 
2895         if (key[0].digitValue() != -1) {
2896             paragraphNr = key[0].digitValue();
2897         }
2898         else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
2899             paragraphNr = 10 + key[0].unicode() - 'a';
2900         }
2901 
2902         paragraphName[paragraphNr] = key[0].toUpper();
2903         usedParagraphNames.insert(key[0].toLower().cell());
2904         paragraph[paragraphNr].insert(key, c.value());
2905         ++c;
2906     }
2907 
2908     /*
2909       Each paragraph j has a size: paragraph[j].count(). In the
2910       discussion, we will assume paragraphs 0 to 5 will have sizes
2911       3, 1, 4, 1, 5, 9.
2912 
2913       We now want to compute the paragraph offset. Paragraphs 0 to 6
2914       start at offsets 0, 3, 4, 8, 9, 14, 23.
2915     */
2916     int paragraphOffset[NumParagraphs + 1];     // 37 + 1
2917     paragraphOffset[0] = 0;
2918     for (int i=0; i<NumParagraphs; i++)         // i = 0..36
2919         paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count();
2920 
2921     int curParNr = 0;
2922     int curParOffset = 0;
2923     QMap<QChar,QString> cmap;
2924 
2925     /*
2926       Output the alphabet as a row of links.
2927      */
2928     if (includeAlphabet) {
2929         writeStartTag(DT_p);
2930         xmlWriter().writeAttribute("outputclass","alphabet");
2931         for (int i = 0; i < 26; i++) {
2932             QChar ch('a' + i);
2933             if (usedParagraphNames.contains(char('a' + i))) {
2934                 writeStartTag(DT_xref);
2935                 // formathtml
2936                 QString guid = lookupGuid(outFileName(),QString(ch));
2937                 QString attr = outFileName() + QString("#%1").arg(guid);
2938                 xmlWriter().writeAttribute("href", attr);
2939                 xmlWriter().writeCharacters(QString(ch.toUpper()));
2940                 writeEndTag(); // </xref>
2941             }
2942         }
2943         writeEndTag(); // </p>
2944     }
2945 
2946     /*
2947       Output a <p> element to contain all the <dl> elements.
2948      */
2949     writeStartTag(DT_p);
2950     xmlWriter().writeAttribute("outputclass","compactlist");
2951 
2952     for (int i=0; i<classMap.count()-1; i++) {
2953         while ((curParNr < NumParagraphs) &&
2954                (curParOffset == paragraph[curParNr].count())) {
2955             ++curParNr;
2956             curParOffset = 0;
2957         }
2958 
2959         /*
2960           Starting a new paragraph means starting a new <dl>.
2961         */
2962         if (curParOffset == 0) {
2963             if (i > 0) {
2964                 writeEndTag(); // </dlentry>
2965                 writeEndTag(); // </dl>
2966             }
2967             writeStartTag(DT_dl);
2968             writeStartTag(DT_dlentry);
2969             writeStartTag(DT_dt);
2970             if (includeAlphabet) {
2971                 QChar c = paragraphName[curParNr][0].toLower();
2972                 writeGuidAttribute(QString(c));
2973             }
2974             xmlWriter().writeAttribute("outputclass","sublist-header");
2975             xmlWriter().writeCharacters(paragraphName[curParNr]);
2976             writeEndTag(); // </dt>
2977         }
2978 
2979         /*
2980           Output a <dd> for the current offset in the current paragraph.
2981          */
2982         writeStartTag(DT_dd);
2983         if ((curParNr < NumParagraphs) &&
2984             !paragraphName[curParNr].isEmpty()) {
2985             NodeMap::Iterator it;
2986             it = paragraph[curParNr].begin();
2987             for (int i=0; i<curParOffset; i++)
2988                 ++it;
2989 
2990             /*
2991               Previously, we used generateFullName() for this, but we
2992               require some special formatting.
2993             */
2994             writeStartTag(DT_xref);
2995             // formathtml
2996             xmlWriter().writeAttribute("href",linkForNode(it.value(), relative));
2997 
2998             QStringList pieces;
2999             if (it.value()->subType() == Node::QmlClass)
3000                 pieces << it.value()->name();
3001             else
3002                 pieces = fullName(it.value(), relative, marker).split("::");
3003             xmlWriter().writeCharacters(protectEnc(pieces.last()));
3004             writeEndTag(); // </xref>
3005             if (pieces.size() > 1) {
3006                 xmlWriter().writeCharacters(" (");
3007                 generateFullName(it.value()->parent(),relative,marker);
3008                 xmlWriter().writeCharacters(")");
3009             }
3010         }
3011         writeEndTag(); // </dd>
3012         curParOffset++;
3013     }
3014     writeEndTag(); // </dlentry>
3015     writeEndTag(); // </dl>
3016     writeEndTag(); // </p>
3017 }
3018 
3019 /*!
3020   Write XML for a function index to the current XML stream.
3021  */
generateFunctionIndex(const Node * relative,CodeMarker * marker)3022 void DitaXmlGenerator::generateFunctionIndex(const Node* relative,
3023                                              CodeMarker* marker)
3024 {
3025     writeStartTag(DT_p);
3026     xmlWriter().writeAttribute("outputclass","alphabet");
3027     for (int i = 0; i < 26; i++) {
3028         QChar ch('a' + i);
3029         writeStartTag(DT_xref);
3030         // formathtml
3031         QString guid = lookupGuid(outFileName(),QString(ch));
3032         QString attr = outFileName() + QString("#%1").arg(guid);
3033         xmlWriter().writeAttribute("href", attr);
3034         xmlWriter().writeCharacters(QString(ch.toUpper()));
3035         writeEndTag(); // </xref>
3036 
3037     }
3038     writeEndTag(); // </p>
3039 
3040     char nextLetter = 'a';
3041     char currentLetter;
3042 
3043     writeStartTag(DT_ul);
3044     QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin();
3045     while (f != funcIndex.end()) {
3046         writeStartTag(DT_li);
3047         currentLetter = f.key()[0].unicode();
3048         while (islower(currentLetter) && currentLetter >= nextLetter) {
3049             writeStartTag(DT_p);
3050             writeGuidAttribute(QString(nextLetter));
3051             xmlWriter().writeAttribute("outputclass","target");
3052             xmlWriter().writeCharacters(QString(nextLetter));
3053             writeEndTag(); // </p>
3054             nextLetter++;
3055         }
3056         xmlWriter().writeCharacters(protectEnc(f.key()));
3057         xmlWriter().writeCharacters(":");
3058 
3059         NodeMap::ConstIterator s = (*f).begin();
3060         while (s != (*f).end()) {
3061             generateFullName((*s)->parent(), relative, marker, *s);
3062             ++s;
3063         }
3064         writeEndTag(); // </li>
3065         ++f;
3066     }
3067     writeEndTag(); // </ul>
3068 }
3069 
3070 /*!
3071   Write the legalese texts as XML to the current XML stream.
3072  */
generateLegaleseList(const Node * relative,CodeMarker * marker)3073 void DitaXmlGenerator::generateLegaleseList(const Node* relative,
3074                                             CodeMarker* marker)
3075 {
3076     QMap<Text, const Node*>::ConstIterator it = legaleseTexts.begin();
3077     while (it != legaleseTexts.end()) {
3078         Text text = it.key();
3079         generateText(text, relative, marker);
3080         writeStartTag(DT_ul);
3081         do {
3082             writeStartTag(DT_li);
3083             generateFullName(it.value(), relative, marker);
3084             writeEndTag(); // </li>
3085             ++it;
3086         } while (it != legaleseTexts.end() && it.key() == text);
3087         writeEndTag(); //</ul>
3088     }
3089 }
3090 
3091 /*!
3092   Generate the text for the QML item described by \a node
3093   and write it to the current XML stream.
3094  */
generateQmlItem(const Node * node,const Node * relative,CodeMarker * marker,bool summary)3095 void DitaXmlGenerator::generateQmlItem(const Node* node,
3096                                        const Node* relative,
3097                                        CodeMarker* marker,
3098                                        bool summary)
3099 {
3100     QString marked = marker->markedUpQmlItem(node,summary);
3101     QRegExp tag("(<[^@>]*>)");
3102     if (marked.indexOf(tag) != -1) {
3103         QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3104         marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3105     }
3106     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3107                    "<i>\\1<sub>\\2</sub></i>");
3108     marked.replace("<@param>", "<i>");
3109     marked.replace("</@param>", "</i>");
3110 
3111     if (summary)
3112         marked.replace("@name>", "b>");
3113 
3114     marked.replace("<@extra>", "<tt>");
3115     marked.replace("</@extra>", "</tt>");
3116 
3117     if (summary) {
3118         marked.replace("<@type>", "");
3119         marked.replace("</@type>", "");
3120     }
3121     writeText(marked, marker, relative);
3122 }
3123 
3124 /*!
3125   Writher the XML for the overview list to the current XML stream.
3126  */
generateOverviewList(const Node * relative,CodeMarker *)3127 void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */)
3128 {
3129     QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap;
3130     QMap<QString, const FakeNode*> groupTitlesMap;
3131     QMap<QString, FakeNode*> uncategorizedNodeMap;
3132     QRegExp singleDigit("\\b([0-9])\\b");
3133 
3134     const NodeList children = myTree->root()->childNodes();
3135     foreach (Node* child, children) {
3136         if (child->type() == Node::Fake && child != relative) {
3137             FakeNode* fakeNode = static_cast<FakeNode*>(child);
3138 
3139             // Check whether the page is part of a group or is the group
3140             // definition page.
3141             QString group;
3142             bool isGroupPage = false;
3143             if (fakeNode->doc().metaCommandsUsed().contains("group")) {
3144                 group = fakeNode->doc().metaCommandArgs("group")[0];
3145                 isGroupPage = true;
3146             }
3147 
3148             // there are too many examples; they would clutter the list
3149             if (fakeNode->subType() == Node::Example)
3150                 continue;
3151 
3152             // not interested either in individual (Qt Designer etc.) manual chapters
3153             if (fakeNode->links().contains(Node::ContentsLink))
3154                 continue;
3155 
3156             // Discard external nodes.
3157             if (fakeNode->subType() == Node::ExternalPage)
3158                 continue;
3159 
3160             QString sortKey = fakeNode->fullTitle().toLower();
3161             if (sortKey.startsWith("the "))
3162                 sortKey.remove(0, 4);
3163             sortKey.replace(singleDigit, "0\\1");
3164 
3165             if (!group.isEmpty()) {
3166                 if (isGroupPage) {
3167                     // If we encounter a group definition page, we add all
3168                     // the pages in that group to the list for that group.
3169                     foreach (Node* member, fakeNode->groupMembers()) {
3170                         if (member->type() != Node::Fake)
3171                             continue;
3172                         FakeNode* page = static_cast<FakeNode*>(member);
3173                         if (page) {
3174                             QString sortKey = page->fullTitle().toLower();
3175                             if (sortKey.startsWith("the "))
3176                                 sortKey.remove(0, 4);
3177                             sortKey.replace(singleDigit, "0\\1");
3178                             fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page);
3179                             groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode);
3180                         }
3181                     }
3182                 }
3183                 else if (!isGroupPage) {
3184                     // If we encounter a page that belongs to a group then
3185                     // we add that page to the list for that group.
3186                     const FakeNode* groupNode =
3187                         static_cast<const FakeNode*>(myTree->root()->findNode(group, Node::Fake));
3188                     if (groupNode)
3189                         fakeNodeMap[groupNode].insert(sortKey, fakeNode);
3190                     //else
3191                     //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3192                 }// else
3193                 //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3194             }// else
3195             //    uncategorizedNodeMap.insert(sortKey, fakeNode);
3196         }
3197     }
3198 
3199     // We now list all the pages found that belong to groups.
3200     // If only certain pages were found for a group, but the definition page
3201     // for that group wasn't listed, the list of pages will be intentionally
3202     // incomplete. However, if the group definition page was listed, all the
3203     // pages in that group are listed for completeness.
3204 
3205     if (!fakeNodeMap.isEmpty()) {
3206         foreach (const QString& groupTitle, groupTitlesMap.keys()) {
3207             const FakeNode* groupNode = groupTitlesMap[groupTitle];
3208             writeStartTag(DT_p);
3209             xmlWriter().writeAttribute("outputclass","h3");
3210             writeStartTag(DT_xref);
3211             // formathtml
3212             xmlWriter().writeAttribute("href",linkForNode(groupNode, relative));
3213             writeCharacters(protectEnc(groupNode->fullTitle()));
3214             writeEndTag(); // </xref>
3215             writeEndTag(); // </p>
3216             if (fakeNodeMap[groupNode].count() == 0)
3217                 continue;
3218 
3219             writeStartTag(DT_ul);
3220             foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) {
3221                 QString title = fakeNode->fullTitle();
3222                 if (title.startsWith("The "))
3223                     title.remove(0, 4);
3224                 writeStartTag(DT_li);
3225                 writeStartTag(DT_xref);
3226                 // formathtml
3227                 xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3228                 writeCharacters(protectEnc(title));
3229                 writeEndTag(); // </xref>
3230                 writeEndTag(); // </li>
3231             }
3232             writeEndTag(); // </ul>
3233         }
3234     }
3235 
3236     if (!uncategorizedNodeMap.isEmpty()) {
3237         writeStartTag(DT_p);
3238         xmlWriter().writeAttribute("outputclass","h3");
3239         xmlWriter().writeCharacters("Miscellaneous");
3240         writeEndTag(); // </p>
3241         writeStartTag(DT_ul);
3242         foreach (const FakeNode *fakeNode, uncategorizedNodeMap) {
3243             QString title = fakeNode->fullTitle();
3244             if (title.startsWith("The "))
3245                 title.remove(0, 4);
3246             writeStartTag(DT_li);
3247             writeStartTag(DT_xref);
3248             // formathtml
3249             xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative));
3250             writeCharacters(protectEnc(title));
3251             writeEndTag(); // </xref>
3252             writeEndTag(); // </li>
3253         }
3254         writeEndTag(); // </ul>
3255     }
3256 }
3257 
3258 /*!
3259   Write the XML for a standard section of a page, e.g.
3260   "Public Functions" or "Protected Slots." The section
3261   is written too the current XML stream as a table.
3262  */
generateSection(const NodeList & nl,const Node * relative,CodeMarker * marker,CodeMarker::SynopsisStyle style)3263 void DitaXmlGenerator::generateSection(const NodeList& nl,
3264                                        const Node* relative,
3265                                        CodeMarker* marker,
3266                                        CodeMarker::SynopsisStyle style)
3267 {
3268     if (!nl.isEmpty()) {
3269         writeStartTag(DT_ul);
3270         NodeList::ConstIterator m = nl.begin();
3271         while (m != nl.end()) {
3272             if ((*m)->access() != Node::Private) {
3273                 writeStartTag(DT_li);
3274                 QString marked = getMarkedUpSynopsis(*m, relative, marker, style);
3275                 writeText(marked, marker, relative);
3276                 writeEndTag(); // </li>
3277             }
3278             ++m;
3279         }
3280         writeEndTag(); // </ul>
3281     }
3282 }
3283 
3284 /*!
3285   Writes the "inherited from" list to the current XML stream.
3286  */
generateSectionInheritedList(const Section & section,const Node * relative,CodeMarker * marker)3287 void DitaXmlGenerator::generateSectionInheritedList(const Section& section,
3288                                                     const Node* relative,
3289                                                     CodeMarker* marker)
3290 {
3291     if (section.inherited.isEmpty())
3292         return;
3293     writeStartTag(DT_ul);
3294     QList<QPair<ClassNode*,int> >::ConstIterator p = section.inherited.begin();
3295     while (p != section.inherited.end()) {
3296         writeStartTag(DT_li);
3297         QString text;
3298         text.setNum((*p).second);
3299         text += " ";
3300         if ((*p).second == 1)
3301             text += section.singularMember;
3302         else
3303             text += section.pluralMember;
3304         text += " inherited from ";
3305         writeCharacters(text);
3306         writeStartTag(DT_xref);
3307         // formathtml
3308         // zzz
3309         text = fileName((*p).first) + "#";
3310         text += DitaXmlGenerator::cleanRef(section.name.toLower());
3311         xmlWriter().writeAttribute("href",text);
3312         text = protectEnc(marker->plainFullName((*p).first, relative));
3313         writeCharacters(text);
3314         writeEndTag(); // </xref>
3315         writeEndTag(); // </li>
3316         ++p;
3317     }
3318     writeEndTag(); // </ul>
3319 }
3320 
3321 /*!
3322   Get the synopsis from the \a node using the \a relative
3323   node if needed, and mark up the synopsis using \a marker.
3324   Use the style to decide which kind of sysnopsis to build,
3325   normally \c Summary or \c Detailed. Return the marked up
3326   string.
3327  */
getMarkedUpSynopsis(const Node * node,const Node * relative,CodeMarker * marker,CodeMarker::SynopsisStyle style)3328 QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node,
3329                                               const Node* relative,
3330                                               CodeMarker* marker,
3331                                               CodeMarker::SynopsisStyle style)
3332 {
3333     QString marked = marker->markedUpSynopsis(node, relative, style);
3334     QRegExp tag("(<[^@>]*>)");
3335     if (marked.indexOf(tag) != -1) {
3336         QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length()));
3337         marked.replace(tag.pos(1), tag.cap(1).length(), tmp);
3338     }
3339     marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
3340                    "<i>\\1<sub>\\2</sub></i>");
3341 #if 0
3342     marked.replace("<@param>","<i>");
3343     marked.replace("</@param>","</i>");
3344 #endif
3345     if (style == CodeMarker::Summary) {
3346         marked.replace("<@name>","");   // was "<b>"
3347         marked.replace("</@name>","");  // was "</b>"
3348     }
3349 
3350     if (style == CodeMarker::SeparateList) {
3351         QRegExp extraRegExp("<@extra>.*</@extra>");
3352         extraRegExp.setMinimal(true);
3353         marked.replace(extraRegExp,"");
3354     }
3355 #if 0
3356     else {
3357         marked.replace("<@extra>","<tt>");
3358         marked.replace("</@extra>","</tt>");
3359     }
3360 #endif
3361 
3362     if (style != CodeMarker::Detailed) {
3363         marked.replace("<@type>","");
3364         marked.replace("</@type>","");
3365     }
3366     return marked;
3367 }
3368 
3369 /*!
3370   Renamed from highlightedCode() in the html generator. Writes
3371   the \a markedCode to the current XML stream.
3372  */
writeText(const QString & markedCode,CodeMarker * marker,const Node * relative)3373 void DitaXmlGenerator::writeText(const QString& markedCode,
3374                                  CodeMarker* marker,
3375                                  const Node* relative)
3376 {
3377     QString src = markedCode;
3378     QString html;
3379     QStringRef arg;
3380     QStringRef par1;
3381 
3382     const QChar charLangle = '<';
3383     const QChar charAt = '@';
3384 
3385     /*
3386       First strip out all the extraneous markup. The table
3387       below contains the markup we want to keep. Everything
3388       else that begins with "<@" or "</@" is stripped out.
3389      */
3390     static const QString spanTags[] = {
3391         "<@link ",         "<@link ",
3392         "<@type>",         "<@type>",
3393         "<@headerfile>",   "<@headerfile>",
3394         "<@func>",         "<@func>",
3395         "<@func ",         "<@func ",
3396         "<@param>",        "<@param>",
3397         "<@extra>",        "<@extra>",
3398         "</@link>",        "</@link>",
3399         "</@type>",        "</@type>",
3400         "</@headerfile>",  "</@headerfile>",
3401         "</@func>",        "</@func>",
3402         "</@param>",        "</@param>",
3403         "</@extra>",        "</@extra>"
3404     };
3405     for (int i = 0, n = src.size(); i < n;) {
3406         if (src.at(i) == charLangle) {
3407             bool handled = false;
3408             for (int k = 0; k != 13; ++k) {
3409                 const QString & tag = spanTags[2 * k];
3410                 if (tag == QStringRef(&src, i, tag.length())) {
3411                     html += spanTags[2 * k + 1];
3412                     i += tag.length();
3413                     handled = true;
3414                     break;
3415                 }
3416             }
3417             if (!handled) {
3418                 ++i;
3419                 if (src.at(i) == charAt ||
3420                     (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
3421                     // drop 'our' unknown tags (the ones still containing '@')
3422                     while (i < n && src.at(i) != QLatin1Char('>'))
3423                         ++i;
3424                     ++i;
3425                 }
3426                 else {
3427                     // retain all others
3428                     html += charLangle;
3429                 }
3430             }
3431         }
3432         else {
3433             html += src.at(i);
3434             ++i;
3435         }
3436     }
3437 
3438     // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
3439     // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
3440     src = html;
3441     html = QString();
3442     static const QString markTags[] = {
3443         // 0       1         2           3       4        5
3444         "link", "type", "headerfile", "func", "param", "extra"
3445     };
3446 
3447     for (int i = 0, n = src.size(); i < n;) {
3448         if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
3449             i += 2;
3450             for (int k = 0; k != 6; ++k) {
3451                 if (parseArg(src, markTags[k], &i, n, &arg, &par1)) {
3452                     const Node* n = 0;
3453                     if (k == 0) { // <@link>
3454                         if (!html.isEmpty()) {
3455                             writeCharacters(html);
3456                             html.clear();
3457                         }
3458                         n = CodeMarker::nodeForString(par1.toString());
3459                         QString link = linkForNode(n, relative);
3460                         addLink(link, arg);
3461                     }
3462                     else if (k == 4) { // <@param>
3463                         if (!html.isEmpty()) {
3464                             writeCharacters(html);
3465                             html.clear();
3466                         }
3467                         writeStartTag(DT_i);
3468                         writeCharacters(arg.toString());
3469                         writeEndTag(); // </i>
3470                     }
3471                     else if (k == 5) { // <@extra>
3472                         if (!html.isEmpty()) {
3473                             writeCharacters(html);
3474                             html.clear();
3475                         }
3476                         writeStartTag(DT_tt);
3477                         writeCharacters(arg.toString());
3478                         writeEndTag(); // </tt>
3479                     }
3480                     else {
3481                         if (!html.isEmpty()) {
3482                             writeCharacters(html);
3483                             html.clear();
3484                         }
3485                         par1 = QStringRef();
3486                         QString link;
3487                         n = marker->resolveTarget(arg.toString(), myTree, relative);
3488                         if (n && n->subType() == Node::QmlBasicType) {
3489                             if (relative && relative->subType() == Node::QmlClass) {
3490                                 link = linkForNode(n,relative);
3491                                 addLink(link, arg);
3492                             }
3493                             else {
3494                                 //Encountered in snippets for example. Where the text should not be a link.
3495                                 writeCharacters(arg.toString());
3496                             }
3497                         }
3498                         else {
3499                             // (zzz) Is this correct for all cases?
3500                             link = linkForNode(n,relative);
3501                             addLink(link, arg);
3502                         }
3503                     }
3504                     break;
3505                 }
3506             }
3507         }
3508         else {
3509             html += src.at(i++);
3510         }
3511     }
3512 
3513     if (!html.isEmpty()) {
3514         writeCharacters(html);
3515     }
3516 }
3517 
generateLink(const Atom * atom,const Node *,CodeMarker * marker)3518 void DitaXmlGenerator::generateLink(const Atom* atom,
3519                                     const Node* /* relative */,
3520                                     CodeMarker* marker)
3521 {
3522     static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_");
3523 
3524     if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) {
3525         // hack for C++: move () outside of link
3526         int k = funcLeftParen.pos(1);
3527         writeCharacters(protectEnc(atom->string().left(k)));
3528         if (link.isEmpty()) {
3529             if (showBrokenLinks)
3530                 writeEndTag(); // </i>
3531         }
3532         else
3533             writeEndTag(); // </xref>
3534         inLink = false;
3535         writeCharacters(protectEnc(atom->string().mid(k)));
3536     }
3537     else if (marker->recognizeLanguage("Java")) {
3538 	// hack for Java: remove () and use <tt> when appropriate
3539         bool func = atom->string().endsWith("()");
3540         bool tt = (func || atom->string().contains(camelCase));
3541         if (tt)
3542             writeStartTag(DT_tt);
3543         if (func)
3544             writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2)));
3545         else
3546             writeCharacters(protectEnc(atom->string()));
3547         writeEndTag(); // </tt>
3548     }
3549     else
3550         writeCharacters(protectEnc(atom->string()));
3551 }
3552 
cleanRef(const QString & ref)3553 QString DitaXmlGenerator::cleanRef(const QString& ref)
3554 {
3555     QString clean;
3556 
3557     if (ref.isEmpty())
3558         return clean;
3559 
3560     clean.reserve(ref.size() + 20);
3561     const QChar c = ref[0];
3562     const uint u = c.unicode();
3563 
3564     if ((u >= 'a' && u <= 'z') ||
3565         (u >= 'A' && u <= 'Z') ||
3566         (u >= '0' && u <= '9')) {
3567         clean += c;
3568     }
3569     else if (u == '~') {
3570         clean += "dtor.";
3571     }
3572     else if (u == '_') {
3573         clean += "underscore.";
3574     }
3575     else {
3576         clean += "A";
3577     }
3578 
3579     for (int i = 1; i < (int) ref.length(); i++) {
3580         const QChar c = ref[i];
3581         const uint u = c.unicode();
3582         if ((u >= 'a' && u <= 'z') ||
3583             (u >= 'A' && u <= 'Z') ||
3584             (u >= '0' && u <= '9') || u == '-' ||
3585             u == '_' || u == ':' || u == '.') {
3586             clean += c;
3587         }
3588         else if (c.isSpace()) {
3589             clean += "-";
3590         }
3591         else if (u == '!') {
3592             clean += "-not";
3593         }
3594         else if (u == '&') {
3595             clean += "-and";
3596         }
3597         else if (u == '<') {
3598             clean += "-lt";
3599         }
3600         else if (u == '=') {
3601             clean += "-eq";
3602         }
3603         else if (u == '>') {
3604             clean += "-gt";
3605         }
3606         else if (u == '#') {
3607             clean += "#";
3608         }
3609         else {
3610             clean += "-";
3611             clean += QString::number((int)u, 16);
3612         }
3613     }
3614     return clean;
3615 }
3616 
registerRef(const QString & ref)3617 QString DitaXmlGenerator::registerRef(const QString& ref)
3618 {
3619     QString clean = DitaXmlGenerator::cleanRef(ref);
3620 
3621     for (;;) {
3622         QString& prevRef = refMap[clean.toLower()];
3623         if (prevRef.isEmpty()) {
3624             prevRef = ref;
3625             break;
3626         }
3627         else if (prevRef == ref)
3628             break;
3629         clean += "x";
3630     }
3631     return clean;
3632 }
3633 
3634 /*!
3635   Calls protect() with the \a string. Returns the result.
3636  */
protectEnc(const QString & string)3637 QString DitaXmlGenerator::protectEnc(const QString& string)
3638 {
3639     return protect(string, outputEncoding);
3640 }
3641 
protect(const QString & string,const QString &)3642 QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding)
3643 {
3644 #define APPEND(x) \
3645     if (xml.isEmpty()) { \
3646         xml = string; \
3647         xml.truncate(i); \
3648     } \
3649     xml += (x);
3650 
3651     QString xml;
3652     int n = string.length();
3653 
3654     for (int i = 0; i < n; ++i) {
3655         QChar ch = string.at(i);
3656 
3657         if (ch == QLatin1Char('&')) {
3658             APPEND("&amp;");
3659         }
3660         else if (ch == QLatin1Char('<')) {
3661             APPEND("&lt;");
3662         }
3663         else if (ch == QLatin1Char('>')) {
3664             APPEND("&gt;");
3665         }
3666         else if (ch == QLatin1Char('"')) {
3667             APPEND("&quot;");
3668         }
3669 #if 0
3670         else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F) ||
3671                  (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) ||
3672                  (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
3673             // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
3674             APPEND("&#x");
3675             xml += QString::number(ch.unicode(), 16);
3676             xml += QLatin1Char(';');
3677         }
3678 #endif
3679         else {
3680             if (!xml.isEmpty())
3681                 xml += ch;
3682         }
3683     }
3684 
3685     if (!xml.isEmpty())
3686         return xml;
3687     return string;
3688 
3689 #undef APPEND
3690 }
3691 
3692 /*!
3693   Constructs a file name appropriate for the \a node
3694   and returns the file name.
3695  */
fileBase(const Node * node) const3696 QString DitaXmlGenerator::fileBase(const Node* node) const
3697 {
3698     QString result;
3699     result = PageGenerator::fileBase(node);
3700 #if 0
3701     if (!node->isInnerNode()) {
3702         switch (node->status()) {
3703         case Node::Compat:
3704             result += "-qt3";
3705             break;
3706         case Node::Obsolete:
3707             result += "-obsolete";
3708             break;
3709         default:
3710             ;
3711         }
3712     }
3713 #endif
3714     return result;
3715 }
3716 
refForNode(const Node * node)3717 QString DitaXmlGenerator::refForNode(const Node* node)
3718 {
3719     const FunctionNode* func;
3720     const TypedefNode* tdn;
3721     QString ref;
3722 
3723     switch (node->type()) {
3724     case Node::Namespace:
3725     case Node::Class:
3726     default:
3727         break;
3728     case Node::Enum:
3729         ref = node->name() + "-enum";
3730         break;
3731     case Node::Typedef:
3732         tdn = static_cast<const TypedefNode *>(node);
3733         if (tdn->associatedEnum()) {
3734             return refForNode(tdn->associatedEnum());
3735         }
3736         else {
3737             ref = node->name() + "-typedef";
3738         }
3739         break;
3740     case Node::Function:
3741         func = static_cast<const FunctionNode *>(node);
3742         if (func->associatedProperty()) {
3743             return refForNode(func->associatedProperty());
3744         }
3745         else {
3746             ref = func->name();
3747             if (func->overloadNumber() != 1)
3748                 ref += "-" + QString::number(func->overloadNumber());
3749         }
3750         break;
3751     case Node::Fake:
3752         if (node->subType() != Node::QmlPropertyGroup)
3753             break;
3754     case Node::QmlProperty:
3755     case Node::Property:
3756         ref = node->name() + "-prop";
3757         break;
3758     case Node::QmlSignal:
3759         ref = node->name() + "-signal";
3760         break;
3761     case Node::QmlMethod:
3762         ref = node->name() + "-method";
3763         break;
3764     case Node::Variable:
3765         ref = node->name() + "-var";
3766         break;
3767     case Node::Target:
3768         return protectEnc(node->name());
3769     }
3770     return registerRef(ref);
3771 }
3772 
guidForNode(const Node * node)3773 QString DitaXmlGenerator::guidForNode(const Node* node)
3774 {
3775     switch (node->type()) {
3776     case Node::Namespace:
3777     case Node::Class:
3778     default:
3779         break;
3780     case Node::Enum:
3781         return node->guid();
3782     case Node::Typedef:
3783         {
3784             const TypedefNode* tdn = static_cast<const TypedefNode*>(node);
3785             if (tdn->associatedEnum())
3786                 return guidForNode(tdn->associatedEnum());
3787         }
3788         return node->guid();
3789     case Node::Function:
3790         {
3791             const FunctionNode* fn = static_cast<const FunctionNode*>(node);
3792             if (fn->associatedProperty()) {
3793                 return guidForNode(fn->associatedProperty());
3794             }
3795             else {
3796                 QString ref = fn->name();
3797                 if (fn->overloadNumber() != 1) {
3798                     ref += "-" + QString::number(fn->overloadNumber());
3799                 }
3800             }
3801             return fn->guid();
3802         }
3803     case Node::Fake:
3804         if (node->subType() != Node::QmlPropertyGroup)
3805             break;
3806     case Node::QmlProperty:
3807     case Node::Property:
3808         return node->guid();
3809     case Node::QmlSignal:
3810         return node->guid();
3811     case Node::QmlMethod:
3812         return node->guid();
3813     case Node::Variable:
3814         return node->guid();
3815     case Node::Target:
3816         return node->guid();
3817     }
3818     return QString();
3819 }
3820 
3821 /*!
3822   Constructs a file name appropriate for the \a node and returns
3823   it. If the \a node is not a fake node, or if it is a fake node but
3824   it is neither an external page node nor an image node, call the
3825   PageGenerator::fileName() function.
3826  */
fileName(const Node * node)3827 QString DitaXmlGenerator::fileName(const Node* node)
3828 {
3829     if (node->type() == Node::Fake) {
3830         if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage)
3831             return node->name();
3832         if (static_cast<const FakeNode*>(node)->subType() == Node::Image)
3833             return node->name();
3834     }
3835     return PageGenerator::fileName(node);
3836 }
3837 
linkForNode(const Node * node,const Node * relative)3838 QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative)
3839 {
3840     if (node == 0 || node == relative)
3841         return QString();
3842     if (!node->url().isEmpty())
3843         return node->url();
3844     if (fileBase(node).isEmpty())
3845         return QString();
3846     if (node->access() == Node::Private)
3847         return QString();
3848 
3849     QString fn = fileName(node);
3850     QString link = fn;
3851 
3852     if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) {
3853         QString guid = guidForNode(node);
3854         if (relative && fn == fileName(relative) && guid == guidForNode(relative)) {
3855             return QString();
3856         }
3857         link += "#";
3858         link += guid;
3859     }
3860     return link;
3861 }
3862 
refForAtom(Atom * atom,const Node *)3863 QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */)
3864 {
3865     if (atom->type() == Atom::SectionLeft)
3866         return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
3867     if (atom->type() == Atom::Target)
3868         return Doc::canonicalTitle(atom->string());
3869     return QString();
3870 }
3871 
generateFullName(const Node * apparentNode,const Node * relative,CodeMarker * marker,const Node * actualNode)3872 void DitaXmlGenerator::generateFullName(const Node* apparentNode,
3873                                         const Node* relative,
3874                                         CodeMarker* marker,
3875                                         const Node* actualNode)
3876 {
3877     if (actualNode == 0)
3878         actualNode = apparentNode;
3879     writeStartTag(DT_xref);
3880     // formathtml
3881     QString href = linkForNode(actualNode, relative);
3882     xmlWriter().writeAttribute("href",href);
3883     writeCharacters(protectEnc(fullName(apparentNode, relative, marker)));
3884     writeEndTag(); // </xref>
3885 }
3886 
findAllClasses(const InnerNode * node)3887 void DitaXmlGenerator::findAllClasses(const InnerNode* node)
3888 {
3889     NodeList::const_iterator c = node->childNodes().constBegin();
3890     while (c != node->childNodes().constEnd()) {
3891         if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) {
3892             if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) {
3893                 QString className = (*c)->name();
3894                 if ((*c)->parent() &&
3895                     (*c)->parent()->type() == Node::Namespace &&
3896                     !(*c)->parent()->name().isEmpty())
3897                     className = (*c)->parent()->name()+"::"+className;
3898 
3899                 if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) {
3900                     if ((*c)->status() == Node::Compat) {
3901                         compatClasses.insert(className, *c);
3902                     }
3903                     else if ((*c)->status() == Node::Obsolete) {
3904                         obsoleteClasses.insert(className, *c);
3905                     }
3906                     else {
3907                         nonCompatClasses.insert(className, *c);
3908                         if ((*c)->status() == Node::Main)
3909                             mainClasses.insert(className, *c);
3910                     }
3911                 }
3912 
3913                 QString moduleName = (*c)->moduleName();
3914                 if (moduleName == "Qt3SupportLight") {
3915                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3916                     moduleName = "Qt3Support";
3917                 }
3918                 if (!moduleName.isEmpty())
3919                     moduleClassMap[moduleName].insert((*c)->name(), *c);
3920 
3921                 QString serviceName =
3922                     (static_cast<const ClassNode *>(*c))->serviceName();
3923                 if (!serviceName.isEmpty())
3924                     serviceClasses.insert(serviceName, *c);
3925             }
3926             else if ((*c)->type() == Node::Fake &&
3927                      (*c)->subType() == Node::QmlClass &&
3928                      !(*c)->doc().isEmpty()) {
3929                 QString qmlClassName = (*c)->name();
3930                 qmlClasses.insert(qmlClassName,*c);
3931             }
3932             else if ((*c)->isInnerNode()) {
3933                 findAllClasses(static_cast<InnerNode *>(*c));
3934             }
3935         }
3936         ++c;
3937     }
3938 }
3939 
findAllFunctions(const InnerNode * node)3940 void DitaXmlGenerator::findAllFunctions(const InnerNode* node)
3941 {
3942     NodeList::ConstIterator c = node->childNodes().begin();
3943     while (c != node->childNodes().end()) {
3944         if ((*c)->access() != Node::Private) {
3945             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3946                 findAllFunctions(static_cast<const InnerNode*>(*c));
3947             }
3948             else if ((*c)->type() == Node::Function) {
3949                 const FunctionNode* func = static_cast<const FunctionNode*>(*c);
3950                 if ((func->status() > Node::Obsolete) &&
3951                     !func->isInternal() &&
3952                     (func->metaness() != FunctionNode::Ctor) &&
3953                     (func->metaness() != FunctionNode::Dtor)) {
3954                     funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c);
3955                 }
3956             }
3957         }
3958         ++c;
3959     }
3960 }
3961 
findAllLegaleseTexts(const InnerNode * node)3962 void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node)
3963 {
3964     NodeList::ConstIterator c = node->childNodes().begin();
3965     while (c != node->childNodes().end()) {
3966         if ((*c)->access() != Node::Private) {
3967             if (!(*c)->doc().legaleseText().isEmpty())
3968                 legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c);
3969             if ((*c)->isInnerNode())
3970                 findAllLegaleseTexts(static_cast<const InnerNode *>(*c));
3971         }
3972         ++c;
3973     }
3974 }
3975 
findAllNamespaces(const InnerNode * node)3976 void DitaXmlGenerator::findAllNamespaces(const InnerNode* node)
3977 {
3978     NodeList::ConstIterator c = node->childNodes().begin();
3979     while (c != node->childNodes().end()) {
3980         if ((*c)->access() != Node::Private) {
3981             if ((*c)->isInnerNode() && (*c)->url().isEmpty()) {
3982                 findAllNamespaces(static_cast<const InnerNode *>(*c));
3983                 if ((*c)->type() == Node::Namespace) {
3984                     const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c);
3985                     // Ensure that the namespace's name is not empty (the root
3986                     // namespace has no name).
3987                     if (!nspace->name().isEmpty()) {
3988                         namespaceIndex.insert(nspace->name(), *c);
3989                         QString moduleName = (*c)->moduleName();
3990                         if (moduleName == "Qt3SupportLight") {
3991                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3992                             moduleName = "Qt3Support";
3993                         }
3994                         if (!moduleName.isEmpty())
3995                             moduleNamespaceMap[moduleName].insert((*c)->name(), *c);
3996                     }
3997                 }
3998             }
3999         }
4000         ++c;
4001     }
4002 }
4003 
4004 /*!
4005   We're writing an attribute that indicates that the text
4006   data is a heading, hence, h1, h2, h3... etc, and we must
4007   decide which number to use.
4008  */
hOffset(const Node * node)4009 int DitaXmlGenerator::hOffset(const Node* node)
4010 {
4011     switch (node->type()) {
4012     case Node::Namespace:
4013     case Node::Class:
4014         return 2;
4015     case Node::Fake:
4016         return 1;
4017     case Node::Enum:
4018     case Node::Typedef:
4019     case Node::Function:
4020     case Node::Property:
4021     default:
4022         return 3;
4023     }
4024 }
4025 
isThreeColumnEnumValueTable(const Atom * atom)4026 bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom)
4027 {
4028     while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
4029         if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight))
4030             return true;
4031         atom = atom->next();
4032     }
4033     return false;
4034 }
4035 
findNodeForTarget(const QString & target,const Node * relative,CodeMarker * marker,const Atom * atom)4036 const Node* DitaXmlGenerator::findNodeForTarget(const QString& target,
4037                                                 const Node* relative,
4038                                                 CodeMarker* marker,
4039                                                 const Atom* atom)
4040 {
4041     const Node* node = 0;
4042 
4043     if (target.isEmpty()) {
4044         node = relative;
4045     }
4046     else if (target.endsWith(".html")) {
4047         node = myTree->root()->findNode(target, Node::Fake);
4048     }
4049     else if (marker) {
4050         node = marker->resolveTarget(target, myTree, relative);
4051         if (!node)
4052             node = myTree->findFakeNodeByTitle(target);
4053         if (!node && atom) {
4054             node = myTree->findUnambiguousTarget(target,
4055                 *const_cast<Atom**>(&atom));
4056         }
4057     }
4058 
4059     if (!node)
4060         relative->doc().location().warning(tr("Cannot link to '%1'").arg(target));
4061 
4062     return node;
4063 }
4064 
anchorForNode(const Node * node)4065 const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node)
4066 {
4067     QPair<QString,QString> anchorPair;
4068     anchorPair.first = PageGenerator::fileName(node);
4069     if (node->type() == Node::Fake) {
4070         const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
4071         anchorPair.second = fakeNode->title();
4072     }
4073 
4074     return anchorPair;
4075 }
4076 
getLink(const Atom * atom,const Node * relative,CodeMarker * marker,const Node ** node)4077 QString DitaXmlGenerator::getLink(const Atom* atom,
4078                                   const Node* relative,
4079                                   CodeMarker* marker,
4080                                   const Node** node)
4081 {
4082     QString link;
4083     *node = 0;
4084     inObsoleteLink = false;
4085 
4086     if (atom->string().contains(":") &&
4087         (atom->string().startsWith("file:")
4088          || atom->string().startsWith("http:")
4089          || atom->string().startsWith("https:")
4090          || atom->string().startsWith("ftp:")
4091          || atom->string().startsWith("mailto:"))) {
4092 
4093         link = atom->string();
4094     }
4095     else {
4096         QStringList path;
4097         if (atom->string().contains('#'))
4098             path = atom->string().split('#');
4099         else
4100             path.append(atom->string());
4101 
4102         Atom* targetAtom = 0;
4103         QString first = path.first().trimmed();
4104 
4105         if (first.isEmpty()) {
4106             *node = relative;
4107         }
4108         else if (first.endsWith(".html")) {
4109             *node = myTree->root()->findNode(first, Node::Fake);
4110         }
4111         else {
4112             *node = marker->resolveTarget(first, myTree, relative);
4113             if (!*node) {
4114                 *node = myTree->findFakeNodeByTitle(first);
4115             }
4116             if (!*node) {
4117                 *node = myTree->findUnambiguousTarget(first, targetAtom);
4118             }
4119         }
4120 
4121         if (*node) {
4122             if (!(*node)->url().isEmpty()) {
4123                 return (*node)->url();
4124             }
4125             else {
4126                 path.removeFirst();
4127             }
4128         }
4129         else {
4130             *node = relative;
4131         }
4132 
4133         if (*node && (*node)->status() == Node::Obsolete) {
4134             if (relative && (relative->parent() != *node) &&
4135                 (relative->status() != Node::Obsolete)) {
4136                 bool porting = false;
4137                 if (relative->type() == Node::Fake) {
4138                     const FakeNode* fake = static_cast<const FakeNode*>(relative);
4139                     if (fake->title().startsWith("Porting"))
4140                         porting = true;
4141                 }
4142                 QString name = marker->plainFullName(relative);
4143                 if (!porting && !name.startsWith("Q3")) {
4144                     if (obsoleteLinks) {
4145                         relative->doc().location().warning(tr("Link to obsolete item '%1' in %2")
4146                                                            .arg(atom->string())
4147                                                            .arg(name));
4148                     }
4149                     inObsoleteLink = true;
4150                 }
4151             }
4152         }
4153 
4154         while (!path.isEmpty()) {
4155             targetAtom = myTree->findTarget(path.first(), *node);
4156             if (targetAtom == 0)
4157                 break;
4158             path.removeFirst();
4159         }
4160 
4161         if (path.isEmpty()) {
4162             link = linkForNode(*node, relative);
4163             if (*node && (*node)->subType() == Node::Image)
4164                 link = "images/used-in-examples/" + link;
4165             if (targetAtom) {
4166                 if (link.isEmpty())
4167                     link = outFileName();
4168                 QString guid = lookupGuid(link,refForAtom(targetAtom,*node));
4169                 link += "#" + guid;
4170             }
4171 #if 0
4172             else if (link.isEmpty() && *node) {
4173                 link = outFileName() + "#" + (*node)->guid();
4174             }
4175 #endif
4176             else if (!link.isEmpty() && *node && link.endsWith(".xml")) {
4177                 link += "#" + (*node)->guid();
4178             }
4179         }
4180     }
4181     if (!link.isEmpty() && link[0] == '#') {
4182         link.prepend(outFileName());
4183         qDebug() << "LOCAL LINK:" << link;
4184     }
4185     return link;
4186 }
4187 
generateIndex(const QString & fileBase,const QString & url,const QString & title)4188 void DitaXmlGenerator::generateIndex(const QString& fileBase,
4189                                      const QString& url,
4190                                      const QString& title)
4191 {
4192     myTree->generateIndex(outputDir() + "/" + fileBase + ".index", url, title);
4193 }
4194 
generateStatus(const Node * node,CodeMarker * marker)4195 void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker)
4196 {
4197     Text text;
4198 
4199     switch (node->status()) {
4200     case Node::Obsolete:
4201         if (node->isInnerNode())
4202 	    Generator::generateStatus(node, marker);
4203         break;
4204     case Node::Compat:
4205         if (node->isInnerNode()) {
4206             text << Atom::ParaLeft
4207                  << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
4208                  << "This "
4209                  << typeString(node)
4210                  << " is part of the Qt 3 support library."
4211                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
4212                  << " It is provided to keep old source code working. "
4213                  << "We strongly advise against "
4214                  << "using it in new code. See ";
4215 
4216             const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4");
4217             Atom *targetAtom = 0;
4218             if (fakeNode && node->type() == Node::Class) {
4219                 QString oldName(node->name());
4220                 targetAtom = myTree->findTarget(oldName.replace("3",""),fakeNode);
4221             }
4222 
4223             if (targetAtom) {
4224                 QString fn = fileName(fakeNode);
4225                 QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode));
4226                 text << Atom(Atom::GuidLink, fn + "#" + guid);
4227             }
4228             else
4229                 text << Atom(Atom::Link, "Porting to Qt 4");
4230 
4231             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
4232                  << Atom(Atom::String, "Porting to Qt 4")
4233                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
4234                  << " for more information."
4235                  << Atom::ParaRight;
4236         }
4237         generateText(text, node, marker);
4238         break;
4239     default:
4240         Generator::generateStatus(node, marker);
4241     }
4242 }
4243 
beginLink(const QString & link)4244 void DitaXmlGenerator::beginLink(const QString& link)
4245 {
4246     this->link = link;
4247     if (link.isEmpty())
4248         return;
4249     writeStartTag(DT_xref);
4250     // formathtml
4251     xmlWriter().writeAttribute("href",link);
4252     inLink = true;
4253 }
4254 
endLink()4255 void DitaXmlGenerator::endLink()
4256 {
4257     if (inLink) {
4258         if (link.isEmpty()) {
4259             if (showBrokenLinks)
4260                 writeEndTag(); // </i>
4261         }
4262         else {
4263             if (inObsoleteLink) {
4264                 writeStartTag(DT_sup);
4265                 xmlWriter().writeCharacters("(obsolete)");
4266                 writeEndTag(); // </sup>
4267             }
4268             writeEndTag(); // </xref>
4269         }
4270     }
4271     inLink = false;
4272     inObsoleteLink = false;
4273 }
4274 
4275 /*!
4276   Generates the summary for the \a section. Only used for
4277   sections of QML element documentation.
4278 
4279   Currently handles only the QML property group.
4280  */
generateQmlSummary(const Section & section,const Node * relative,CodeMarker * marker)4281 void DitaXmlGenerator::generateQmlSummary(const Section& section,
4282                                           const Node* relative,
4283                                           CodeMarker* marker)
4284 {
4285     if (!section.members.isEmpty()) {
4286         writeStartTag(DT_ul);
4287         NodeList::ConstIterator m;
4288         m = section.members.begin();
4289         while (m != section.members.end()) {
4290             writeStartTag(DT_li);
4291             generateQmlItem(*m,relative,marker,true);
4292             writeEndTag(); // </li>
4293             ++m;
4294         }
4295         writeEndTag(); // </ul>
4296     }
4297 }
4298 
4299 /*!
4300   zzz
4301   Outputs the html detailed documentation for a section
4302   on a QML element reference page.
4303  */
generateDetailedQmlMember(const Node * node,const InnerNode * relative,CodeMarker * marker)4304 void DitaXmlGenerator::generateDetailedQmlMember(const Node* node,
4305                                                  const InnerNode* relative,
4306                                                  CodeMarker* marker)
4307 {
4308     QString marked;
4309     const QmlPropertyNode* qpn = 0;
4310     if (node->subType() == Node::QmlPropertyGroup) {
4311         const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node);
4312         NodeList::ConstIterator p = qpgn->childNodes().begin();
4313         writeStartTag(DT_ul);
4314         while (p != qpgn->childNodes().end()) {
4315             if ((*p)->type() == Node::QmlProperty) {
4316                 qpn = static_cast<const QmlPropertyNode*>(*p);
4317                 writeStartTag(DT_li);
4318                 writeGuidAttribute((Node*)qpn);
4319                 QString attr;
4320                 const ClassNode* cn = qpn->declarativeCppNode();
4321                 if (cn && !qpn->isWritable(myTree))
4322                     attr = "read-only";
4323                 if (qpgn->isDefault()) {
4324                     if (!attr.isEmpty())
4325                         attr += " ";
4326                     attr += "default";
4327                 }
4328                 if (!attr.isEmpty())
4329                     xmlWriter().writeAttribute("outputclass",attr);
4330                 generateQmlItem(qpn, relative, marker, false);
4331                 writeEndTag(); // </li>
4332             }
4333             ++p;
4334         }
4335         writeEndTag(); // </ul>
4336     }
4337     else if (node->type() == Node::QmlSignal) {
4338         Node* n = const_cast<Node*>(node);
4339         writeStartTag(DT_ul);
4340         writeStartTag(DT_li);
4341         writeGuidAttribute(n);
4342         marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4343         writeText(marked, marker, relative);
4344         writeEndTag(); // </li>
4345         writeEndTag(); // </ul>
4346     }
4347     else if (node->type() == Node::QmlMethod) {
4348         Node* n = const_cast<Node*>(node);
4349         writeStartTag(DT_ul);
4350         writeStartTag(DT_li);
4351         writeGuidAttribute(n);
4352         marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed);
4353         writeText(marked, marker, relative);
4354         writeEndTag(); // </li>
4355         writeEndTag(); // </ul>
4356     }
4357     generateStatus(node, marker);
4358     generateBody(node, marker);
4359     generateThreadSafeness(node, marker);
4360     generateSince(node, marker);
4361     generateAlsoList(node, marker);
4362 }
4363 
4364 /*!
4365   Output the "Inherits" line for the QML element,
4366   if there should be one.
4367  */
generateQmlInherits(const QmlClassNode * cn,CodeMarker * marker)4368 void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* cn,
4369                                            CodeMarker* marker)
4370 {
4371     if (cn && !cn->links().empty()) {
4372         if (cn->links().contains(Node::InheritsLink)) {
4373             QPair<QString,QString> linkPair;
4374             linkPair = cn->links()[Node::InheritsLink];
4375             QStringList strList(linkPair.first);
4376             const Node* n = myTree->findNode(strList,Node::Fake);
4377             if (n && n->subType() == Node::QmlClass) {
4378                 const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
4379                 writeStartTag(DT_p);
4380                 xmlWriter().writeAttribute("outputclass","inherits");
4381                 Text text;
4382                 text << "[Inherits ";
4383                 text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4384                 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4385                 text << Atom(Atom::String, linkPair.second);
4386                 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4387                 text << "]";
4388                 generateText(text, cn, marker);
4389                 writeEndTag(); // </p>
4390             }
4391         }
4392     }
4393 }
4394 
4395 /*!
4396   Output the "Inherit by" list for the QML element,
4397   if it is inherited by any other elements.
4398  */
generateQmlInheritedBy(const QmlClassNode * cn,CodeMarker * marker)4399 void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* cn,
4400                                               CodeMarker* marker)
4401 {
4402     if (cn) {
4403         NodeList subs;
4404         QmlClassNode::subclasses(cn->name(),subs);
4405         if (!subs.isEmpty()) {
4406             Text text;
4407             text << Atom::ParaLeft << "Inherited by ";
4408             appendSortedQmlNames(text,cn,subs,marker);
4409             text << Atom::ParaRight;
4410             generateText(text, cn, marker);
4411         }
4412     }
4413 }
4414 
4415 /*!
4416   Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]"
4417   line for the QML element, if there should be one.
4418 
4419   If there is no class node, or if the class node status
4420   is set to Node::Internal, do nothing.
4421  */
generateQmlInstantiates(const QmlClassNode * qcn,CodeMarker * marker)4422 void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn,
4423                                                CodeMarker* marker)
4424 {
4425     const ClassNode* cn = qcn->classNode();
4426     if (cn && (cn->status() != Node::Internal)) {
4427         writeStartTag(DT_p);
4428             xmlWriter().writeAttribute("outputclass","instantiates");
4429         Text text;
4430         text << "[";
4431         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn));
4432         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4433         text << Atom(Atom::String, qcn->name());
4434         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4435         text << " instantiates the C++ class ";
4436         text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4437         text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4438         text << Atom(Atom::String, cn->name());
4439         text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4440         text << "]";
4441         generateText(text, qcn, marker);
4442         writeEndTag(); // </p>
4443     }
4444 }
4445 
4446 /*!
4447   Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]"
4448   line for the class, if there should be one.
4449 
4450   If there is no QML element, or if the class node status
4451   is set to Node::Internal, do nothing.
4452  */
generateInstantiatedBy(const ClassNode * cn,CodeMarker * marker)4453 void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn,
4454                                               CodeMarker* marker)
4455 {
4456     if (cn &&  cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) {
4457         const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake);
4458         if (n && n->subType() == Node::QmlClass) {
4459             writeStartTag(DT_p);
4460             xmlWriter().writeAttribute("outputclass","instantiated-by");
4461             Text text;
4462             text << "[";
4463             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn));
4464             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4465             text << Atom(Atom::String, cn->name());
4466             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4467             text << " is instantiated by QML element ";
4468             text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n));
4469             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
4470             text << Atom(Atom::String, n->name());
4471             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
4472             text << "]";
4473             generateText(text, cn, marker);
4474             writeEndTag(); // </p>
4475         }
4476     }
4477 }
4478 
4479 /*!
4480   Return the full qualification of the node \a n, but without
4481   the name of \a n itself. e.g. A::B::C
4482  */
fullQualification(const Node * n)4483 QString DitaXmlGenerator::fullQualification(const Node* n)
4484 {
4485     QString fq;
4486     InnerNode* in = n->parent();
4487     while (in) {
4488         if ((in->type() == Node::Class) ||
4489             (in->type() == Node::Namespace)) {
4490             if (in->name().isEmpty())
4491                 break;
4492             if (fq.isEmpty())
4493                 fq = in->name();
4494             else
4495                 fq = in->name() + "::" + fq;
4496         }
4497         else
4498             break;
4499         in = in->parent();
4500     }
4501     return fq;
4502 }
4503 
4504 /*!
4505   Outputs the <cxxClassDerivations> element.
4506   \code
4507 	<cxxClassDerivations>
4508 		<cxxClassDerivation>
4509 			...
4510 		</cxxClassDerivation>
4511 		...
4512 	</cxxClassDerivations>
4513   \endcode
4514 
4515   The <cxxClassDerivation> element is:
4516 
4517   \code
4518 	<cxxClassDerivation>
4519 		<cxxClassDerivationAccessSpecifier value="public"/>
4520 		<cxxClassBaseClass href="class_base">Base</cxxClassBaseClass>
4521 	</cxxClassDerivation>
4522   \endcode
4523  */
writeDerivations(const ClassNode * cn,CodeMarker * marker)4524 void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker)
4525 {
4526     QList<RelatedClass>::ConstIterator r;
4527 
4528     if (!cn->baseClasses().isEmpty()) {
4529         writeStartTag(DT_cxxClassDerivations);
4530         r = cn->baseClasses().begin();
4531         while (r != cn->baseClasses().end()) {
4532             writeStartTag(DT_cxxClassDerivation);
4533             writeStartTag(DT_cxxClassDerivationAccessSpecifier);
4534             xmlWriter().writeAttribute("value",(*r).accessString());
4535             writeEndTag(); // </cxxClassDerivationAccessSpecifier>
4536 
4537             // not included: <cxxClassDerivationVirtual>
4538 
4539             writeStartTag(DT_cxxClassBaseClass);
4540             QString attr = fileName((*r).node) + "#" + (*r).node->guid();
4541             xmlWriter().writeAttribute("href",attr);
4542             writeCharacters(marker->plainFullName((*r).node));
4543             writeEndTag(); // </cxxClassBaseClass>
4544 
4545             // not included: <ClassBaseStruct> or <cxxClassBaseUnion>
4546 
4547             writeEndTag(); // </cxxClassDerivation>
4548 
4549             // not included: <cxxStructDerivation>
4550 
4551              ++r;
4552         }
4553         writeEndTag(); // </cxxClassDerivations>
4554      }
4555 }
4556 
4557 /*!
4558   Writes a <cxxXXXAPIItemLocation> element, depending on the
4559   type of the node \a n, which can be a class, function, enum,
4560   typedef, or property.
4561  */
writeLocation(const Node * n)4562 void DitaXmlGenerator::writeLocation(const Node* n)
4563 {
4564     DitaTag s1, s2, s3, s4, s5, s6;
4565     s1 = DT_cxxClassAPIItemLocation;
4566     s2 = DT_cxxClassDeclarationFile;
4567     s3 = DT_cxxClassDeclarationFileLine;
4568     s4 = DT_LAST;
4569     if (n->type() == Node::Class || n->type() == Node::Namespace) {
4570         s1 = DT_cxxClassAPIItemLocation;
4571         s2 = DT_cxxClassDeclarationFile;
4572         s3 = DT_cxxClassDeclarationFileLine;
4573     }
4574     else if (n->type() == Node::Function) {
4575         FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n));
4576         if (fn->isMacro()) {
4577             s1 = DT_cxxDefineAPIItemLocation;
4578             s2 = DT_cxxDefineDeclarationFile;
4579             s3 = DT_cxxDefineDeclarationFileLine;
4580         }
4581         else {
4582             s1 = DT_cxxFunctionAPIItemLocation;
4583             s2 = DT_cxxFunctionDeclarationFile;
4584             s3 = DT_cxxFunctionDeclarationFileLine;
4585         }
4586     }
4587     else if (n->type() == Node::Enum) {
4588         s1 = DT_cxxEnumerationAPIItemLocation;
4589         s2 = DT_cxxEnumerationDeclarationFile;
4590         s3 = DT_cxxEnumerationDeclarationFileLine;
4591         s4 = DT_cxxEnumerationDefinitionFile;
4592         s5 = DT_cxxEnumerationDefinitionFileLineStart;
4593         s6 = DT_cxxEnumerationDefinitionFileLineEnd;
4594     }
4595     else if (n->type() == Node::Typedef) {
4596         s1 = DT_cxxTypedefAPIItemLocation;
4597         s2 = DT_cxxTypedefDeclarationFile;
4598         s3 = DT_cxxTypedefDeclarationFileLine;
4599     }
4600     else if ((n->type() == Node::Property) ||
4601              (n->type() == Node::Variable)) {
4602         s1 = DT_cxxVariableAPIItemLocation;
4603         s2 = DT_cxxVariableDeclarationFile;
4604         s3 = DT_cxxVariableDeclarationFileLine;
4605     }
4606     writeStartTag(s1);
4607     writeStartTag(s2);
4608     xmlWriter().writeAttribute("name","filePath");
4609     xmlWriter().writeAttribute("value",n->location().filePath());
4610     writeEndTag(); // </cxx<s2>DeclarationFile>
4611     writeStartTag(s3);
4612     xmlWriter().writeAttribute("name","lineNumber");
4613     QString lineNr;
4614     xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4615     writeEndTag(); // </cxx<s3>DeclarationFileLine>
4616     if (s4 != DT_LAST) { // zzz This stuff is temporary, I think.
4617         writeStartTag(s4);
4618         xmlWriter().writeAttribute("name","filePath");
4619         xmlWriter().writeAttribute("value",n->location().filePath());
4620         writeEndTag(); // </cxx<s4>DefinitionFile>
4621         writeStartTag(s5);
4622         xmlWriter().writeAttribute("name","lineNumber");
4623         xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4624         writeEndTag(); // </cxx<s5>DefinitionFileLineStart>
4625         writeStartTag(s6);
4626         xmlWriter().writeAttribute("name","lineNumber");
4627         xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo()));
4628         writeEndTag(); // </cxx<s6>DefinitionFileLineEnd>
4629     }
4630 
4631     // not included: <cxxXXXDefinitionFile>, <cxxXXXDefinitionFileLineStart>,
4632     //               and <cxxXXXDefinitionFileLineEnd>
4633 
4634     writeEndTag(); // </cxx<s1>ApiItemLocation>
4635 }
4636 
4637 /*!
4638   Write the <cxxFunction> elements.
4639  */
writeFunctions(const Section & s,const InnerNode * parent,CodeMarker * marker,const QString & attribute)4640 void DitaXmlGenerator::writeFunctions(const Section& s,
4641                                       const InnerNode* parent,
4642                                       CodeMarker* marker,
4643                                       const QString& attribute)
4644 {
4645     NodeList::ConstIterator m = s.members.begin();
4646     while (m != s.members.end()) {
4647         if ((*m)->type() == Node::Function) {
4648             FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m));
4649             writeStartTag(DT_cxxFunction);
4650             xmlWriter().writeAttribute("id",fn->guid());
4651             if (!attribute.isEmpty())
4652                 xmlWriter().writeAttribute("outputclass",attribute);
4653             writeStartTag(DT_apiName);
4654             if (fn->metaness() == FunctionNode::Signal)
4655                 xmlWriter().writeAttribute("class","signal");
4656             else if (fn->metaness() == FunctionNode::Slot)
4657                 xmlWriter().writeAttribute("class","slot");
4658             writeCharacters(fn->name());
4659             writeEndTag(); // </apiName>
4660             generateBrief(fn,marker);
4661 
4662             // not included: <prolog>
4663 
4664             writeStartTag(DT_cxxFunctionDetail);
4665             writeStartTag(DT_cxxFunctionDefinition);
4666             writeStartTag(DT_cxxFunctionAccessSpecifier);
4667             xmlWriter().writeAttribute("value",fn->accessString());
4668             writeEndTag(); // <cxxFunctionAccessSpecifier>
4669 
4670             // not included: <cxxFunctionStorageClassSpecifierExtern>
4671 
4672             if (fn->isStatic()) {
4673                 writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic);
4674                 xmlWriter().writeAttribute("name","static");
4675                 xmlWriter().writeAttribute("value","static");
4676                 writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic>
4677             }
4678 
4679             // not included: <cxxFunctionStorageClassSpecifierMutable>,
4680 
4681             if (fn->isConst()) {
4682                 writeStartTag(DT_cxxFunctionConst);
4683                 xmlWriter().writeAttribute("name","const");
4684                 xmlWriter().writeAttribute("value","const");
4685                 writeEndTag(); // <cxxFunctionConst>
4686             }
4687 
4688             // not included: <cxxFunctionExplicit>
4689             //               <cxxFunctionInline
4690 
4691             if (fn->virtualness() != FunctionNode::NonVirtual) {
4692                 writeStartTag(DT_cxxFunctionVirtual);
4693                 xmlWriter().writeAttribute("name","virtual");
4694                 xmlWriter().writeAttribute("value","virtual");
4695                 writeEndTag(); // <cxxFunctionVirtual>
4696                 if (fn->virtualness() == FunctionNode::PureVirtual) {
4697                     writeStartTag(DT_cxxFunctionPureVirtual);
4698                     xmlWriter().writeAttribute("name","pure virtual");
4699                     xmlWriter().writeAttribute("value","pure virtual");
4700                     writeEndTag(); // <cxxFunctionPureVirtual>
4701                 }
4702             }
4703 
4704             if (fn->name() == parent->name()) {
4705                 writeStartTag(DT_cxxFunctionConstructor);
4706                 xmlWriter().writeAttribute("name","constructor");
4707                 xmlWriter().writeAttribute("value","constructor");
4708                 writeEndTag(); // <cxxFunctionConstructor>
4709             }
4710             else if (fn->name()[0] == QChar('~')) {
4711                 writeStartTag(DT_cxxFunctionDestructor);
4712                 xmlWriter().writeAttribute("name","destructor");
4713                 xmlWriter().writeAttribute("value","destructor");
4714                 writeEndTag(); // <cxxFunctionDestructor>
4715             }
4716             else {
4717                 writeStartTag(DT_cxxFunctionDeclaredType);
4718                 QString src = marker->typified(fn->returnType());
4719                 replaceTypesWithLinks(fn,parent,marker,src);
4720                 writeEndTag(); // <cxxFunctionDeclaredType>
4721             }
4722 
4723             // not included: <cxxFunctionReturnType>
4724 
4725             QString fq = fullQualification(fn);
4726             if (!fq.isEmpty()) {
4727                 writeStartTag(DT_cxxFunctionScopedName);
4728                 writeCharacters(fq);
4729                 writeEndTag(); // <cxxFunctionScopedName>
4730             }
4731             writeStartTag(DT_cxxFunctionPrototype);
4732             writeCharacters(fn->signature(true));
4733             writeEndTag(); // <cxxFunctionPrototype>
4734 
4735             QString fnl = fn->signature(false);
4736             int idx = fnl.indexOf(' ');
4737             if (idx < 0)
4738                 idx = 0;
4739             else
4740                 ++idx;
4741             fnl = fn->parent()->name() + "::" + fnl.mid(idx);
4742             writeStartTag(DT_cxxFunctionNameLookup);
4743             writeCharacters(fnl);
4744             writeEndTag(); // <cxxFunctionNameLookup>
4745 
4746             if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) {
4747                 FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
4748                 if (rfn && !rfn->isInternal()) {
4749                     writeStartTag(DT_cxxFunctionReimplemented);
4750                     xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
4751                     writeCharacters(marker->plainFullName(rfn));
4752                     writeEndTag(); // </cxxFunctionReimplemented>
4753                 }
4754             }
4755             writeParameters(fn,parent,marker);
4756             writeLocation(fn);
4757             writeEndTag(); // <cxxFunctionDefinition>
4758 
4759             writeApiDesc(fn, marker, QString());
4760             // generateAlsoList(inner, marker);
4761 
4762             // not included: <example> or <apiImpl>
4763 
4764             writeEndTag(); // </cxxFunctionDetail>
4765             writeEndTag(); // </cxxFunction>
4766 
4767             if (fn->metaness() == FunctionNode::Ctor ||
4768                 fn->metaness() == FunctionNode::Dtor ||
4769                 fn->overloadNumber() != 1) {
4770             }
4771         }
4772         ++m;
4773     }
4774 }
4775 
4776 static const QString typeTag("type");
4777 static const QChar charLangle = '<';
4778 static const QChar charAt = '@';
4779 
4780 /*!
4781   This function replaces class and enum names with <apiRelation>
4782   elements, i.e. links.
4783  */
replaceTypesWithLinks(const Node * n,const InnerNode * parent,CodeMarker * marker,QString & src)4784 void DitaXmlGenerator::replaceTypesWithLinks(const Node* n,
4785                                              const InnerNode* parent,
4786                                              CodeMarker* marker,
4787                                              QString& src)
4788 {
4789     QStringRef arg;
4790     QStringRef par1;
4791     int srcSize = src.size();
4792     QString text;
4793     for (int i=0; i<srcSize;) {
4794         if (src.at(i) == charLangle && src.at(i+1) == charAt) {
4795             if (!text.isEmpty()) {
4796                 writeCharacters(text);
4797                 text.clear();
4798             }
4799             i += 2;
4800             if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
4801                 const Node* tn = marker->resolveTarget(arg.toString(), myTree, parent, n);
4802                 addLink(linkForNode(tn,parent),arg,DT_apiRelation);
4803             }
4804         }
4805         else {
4806             text += src.at(i++);
4807         }
4808     }
4809     if (!text.isEmpty()) {
4810         writeCharacters(text);
4811         text.clear();
4812     }
4813 }
4814 
4815 /*!
4816   This function writes the <cxxFunctionParameters> element.
4817  */
writeParameters(const FunctionNode * fn,const InnerNode * parent,CodeMarker * marker)4818 void DitaXmlGenerator::writeParameters(const FunctionNode* fn,
4819                                        const InnerNode* parent,
4820                                        CodeMarker* marker)
4821 {
4822     const QList<Parameter>& parameters = fn->parameters();
4823     if (!parameters.isEmpty()) {
4824         writeStartTag(DT_cxxFunctionParameters);
4825         QList<Parameter>::ConstIterator p = parameters.begin();
4826         while (p != parameters.end()) {
4827             writeStartTag(DT_cxxFunctionParameter);
4828             writeStartTag(DT_cxxFunctionParameterDeclaredType);
4829             QString src = marker->typified((*p).leftType());
4830             replaceTypesWithLinks(fn,parent,marker,src);
4831             //writeCharacters((*p).leftType());
4832             if (!(*p).rightType().isEmpty())
4833                 writeCharacters((*p).rightType());
4834             writeEndTag(); // <cxxFunctionParameterDeclaredType>
4835             writeStartTag(DT_cxxFunctionParameterDeclarationName);
4836             writeCharacters((*p).name());
4837             writeEndTag(); // <cxxFunctionParameterDeclarationName>
4838 
4839             // not included: <cxxFunctionParameterDefinitionName>
4840 
4841             if (!(*p).defaultValue().isEmpty()) {
4842                 writeStartTag(DT_cxxFunctionParameterDefaultValue);
4843                 writeCharacters((*p).defaultValue());
4844                 writeEndTag(); // <cxxFunctionParameterDefaultValue>
4845             }
4846 
4847             // not included: <apiDefNote>
4848 
4849             writeEndTag(); // <cxxFunctionParameter>
4850             ++p;
4851         }
4852         writeEndTag(); // <cxxFunctionParameters>
4853     }
4854 }
4855 
4856 /*!
4857   This function writes the enum types.
4858  */
writeEnumerations(const Section & s,CodeMarker * marker,const QString & attribute)4859 void DitaXmlGenerator::writeEnumerations(const Section& s,
4860                                          CodeMarker* marker,
4861                                          const QString& attribute)
4862 {
4863     NodeList::ConstIterator m = s.members.begin();
4864     while (m != s.members.end()) {
4865         if ((*m)->type() == Node::Enum) {
4866             const EnumNode* en = static_cast<const EnumNode*>(*m);
4867             writeStartTag(DT_cxxEnumeration);
4868             xmlWriter().writeAttribute("id",en->guid());
4869             if (!attribute.isEmpty())
4870                 xmlWriter().writeAttribute("outputclass",attribute);
4871             writeStartTag(DT_apiName);
4872             writeCharacters(en->name());
4873             writeEndTag(); // </apiName>
4874             generateBrief(en,marker);
4875 
4876             // not included <prolog>
4877 
4878             writeStartTag(DT_cxxEnumerationDetail);
4879             writeStartTag(DT_cxxEnumerationDefinition);
4880             writeStartTag(DT_cxxEnumerationAccessSpecifier);
4881             xmlWriter().writeAttribute("value",en->accessString());
4882             writeEndTag(); // <cxxEnumerationAccessSpecifier>
4883 
4884             QString fq = fullQualification(en);
4885             if (!fq.isEmpty()) {
4886                 writeStartTag(DT_cxxEnumerationScopedName);
4887                 writeCharacters(fq);
4888                 writeEndTag(); // <cxxEnumerationScopedName>
4889             }
4890             const QList<EnumItem>& items = en->items();
4891             if (!items.isEmpty()) {
4892                 writeStartTag(DT_cxxEnumerationPrototype);
4893                 writeCharacters(en->name());
4894                 xmlWriter().writeCharacters(" = { ");
4895                 QList<EnumItem>::ConstIterator i = items.begin();
4896                 while (i != items.end()) {
4897                     writeCharacters((*i).name());
4898                     if (!(*i).value().isEmpty()) {
4899                         xmlWriter().writeCharacters(" = ");
4900                         writeCharacters((*i).value());
4901                     }
4902                     ++i;
4903                     if (i != items.end())
4904                         xmlWriter().writeCharacters(", ");
4905                 }
4906                 xmlWriter().writeCharacters(" }");
4907                 writeEndTag(); // <cxxEnumerationPrototype>
4908             }
4909 
4910             writeStartTag(DT_cxxEnumerationNameLookup);
4911             writeCharacters(en->parent()->name() + "::" + en->name());
4912             writeEndTag(); // <cxxEnumerationNameLookup>
4913 
4914             // not included: <cxxEnumerationReimplemented>
4915 
4916             if (!items.isEmpty()) {
4917                 writeStartTag(DT_cxxEnumerators);
4918                 QList<EnumItem>::ConstIterator i = items.begin();
4919                 while (i != items.end()) {
4920                     writeStartTag(DT_cxxEnumerator);
4921                     writeStartTag(DT_apiName);
4922                     writeCharacters((*i).name());
4923                     writeEndTag(); // </apiName>
4924 
4925                     QString fq = fullQualification(en->parent());
4926                     if (!fq.isEmpty()) {
4927                         writeStartTag(DT_cxxEnumeratorScopedName);
4928                         writeCharacters(fq + "::" + (*i).name());
4929                         writeEndTag(); // <cxxEnumeratorScopedName>
4930                     }
4931                     writeStartTag(DT_cxxEnumeratorPrototype);
4932                     writeCharacters((*i).name());
4933                     writeEndTag(); // <cxxEnumeratorPrototype>
4934                     writeStartTag(DT_cxxEnumeratorNameLookup);
4935                     writeCharacters(en->parent()->name() + "::" + (*i).name());
4936                     writeEndTag(); // <cxxEnumeratorNameLookup>
4937 
4938                     if (!(*i).value().isEmpty()) {
4939                         writeStartTag(DT_cxxEnumeratorInitialiser);
4940                         xmlWriter().writeAttribute("value", (*i).value());
4941                         writeEndTag(); // <cxxEnumeratorInitialiser>
4942                     }
4943 
4944                     // not included: <cxxEnumeratorAPIItemLocation>
4945 
4946                     if (!(*i).text().isEmpty()) {
4947                         writeStartTag(DT_apiDesc);
4948                         generateText((*i).text(), en, marker);
4949                         writeEndTag(); // </apiDesc>
4950                     }
4951                     writeEndTag(); // <cxxEnumerator>
4952                     ++i;
4953                 }
4954                 writeEndTag(); // <cxxEnumerators>
4955             }
4956 
4957             writeLocation(en);
4958             writeEndTag(); // <cxxEnumerationDefinition>
4959 
4960             writeApiDesc(en, marker, QString());
4961 
4962             // not included: <example> or <apiImpl>
4963 
4964             writeEndTag(); // </cxxEnumerationDetail>
4965 
4966             // not included: <related-links>
4967 
4968             writeEndTag(); // </cxxEnumeration>
4969         }
4970         ++m;
4971     }
4972 }
4973 
4974 /*!
4975   This function writes the output for the \typedef commands.
4976  */
writeTypedefs(const Section & s,CodeMarker * marker,const QString & attribute)4977 void DitaXmlGenerator::writeTypedefs(const Section& s,
4978                                      CodeMarker* marker,
4979                                      const QString& attribute)
4980 
4981 {
4982     NodeList::ConstIterator m = s.members.begin();
4983     while (m != s.members.end()) {
4984         if ((*m)->type() == Node::Typedef) {
4985             const TypedefNode* tn = static_cast<const TypedefNode*>(*m);
4986             writeStartTag(DT_cxxTypedef);
4987             xmlWriter().writeAttribute("id",tn->guid());
4988             if (!attribute.isEmpty())
4989                 xmlWriter().writeAttribute("outputclass",attribute);
4990             writeStartTag(DT_apiName);
4991             writeCharacters(tn->name());
4992             writeEndTag(); // </apiName>
4993             generateBrief(tn,marker);
4994 
4995             // not included: <prolog>
4996 
4997             writeStartTag(DT_cxxTypedefDetail);
4998             writeStartTag(DT_cxxTypedefDefinition);
4999             writeStartTag(DT_cxxTypedefAccessSpecifier);
5000             xmlWriter().writeAttribute("value",tn->accessString());
5001             writeEndTag(); // <cxxTypedefAccessSpecifier>
5002 
5003             // not included: <cxxTypedefDeclaredType>
5004 
5005             QString fq = fullQualification(tn);
5006             if (!fq.isEmpty()) {
5007                 writeStartTag(DT_cxxTypedefScopedName);
5008                 writeCharacters(fq);
5009                 writeEndTag(); // <cxxTypedefScopedName>
5010             }
5011 
5012             // not included: <cxxTypedefPrototype>
5013 
5014             writeStartTag(DT_cxxTypedefNameLookup);
5015             writeCharacters(tn->parent()->name() + "::" + tn->name());
5016             writeEndTag(); // <cxxTypedefNameLookup>
5017 
5018             // not included: <cxxTypedefReimplemented>
5019 
5020             writeLocation(tn);
5021             writeEndTag(); // <cxxTypedefDefinition>
5022 
5023             writeApiDesc(tn, marker, QString());
5024 
5025             // not included: <example> or <apiImpl>
5026 
5027             writeEndTag(); // </cxxTypedefDetail>
5028 
5029             // not included: <related-links>
5030 
5031             writeEndTag(); // </cxxTypedef>
5032         }
5033         ++m;
5034     }
5035 }
5036 
5037 /*!
5038   This function writes the output for the \property commands.
5039   This is the Q_PROPERTYs.
5040  */
writeProperties(const Section & s,CodeMarker * marker,const QString & attribute)5041 void DitaXmlGenerator::writeProperties(const Section& s,
5042                                        CodeMarker* marker,
5043                                        const QString& attribute)
5044 {
5045     NodeList::ConstIterator m = s.members.begin();
5046     while (m != s.members.end()) {
5047         if ((*m)->type() == Node::Property) {
5048             const PropertyNode* pn = static_cast<const PropertyNode*>(*m);
5049             writeStartTag(DT_cxxVariable);
5050             xmlWriter().writeAttribute("id",pn->guid());
5051             if (!attribute.isEmpty())
5052                 xmlWriter().writeAttribute("outputclass",attribute);
5053             writeStartTag(DT_apiName);
5054             writeCharacters(pn->name());
5055             writeEndTag(); // </apiName>
5056             generateBrief(pn,marker);
5057 
5058             // not included: <prolog>
5059 
5060             writeStartTag(DT_cxxVariableDetail);
5061             writeStartTag(DT_cxxVariableDefinition);
5062             writeStartTag(DT_cxxVariableAccessSpecifier);
5063             xmlWriter().writeAttribute("value",pn->accessString());
5064             writeEndTag(); // <cxxVariableAccessSpecifier>
5065 
5066             // not included: <cxxVariableStorageClassSpecifierExtern>,
5067             //               <cxxVariableStorageClassSpecifierStatic>,
5068             //               <cxxVariableStorageClassSpecifierMutable>,
5069             //               <cxxVariableConst>, <cxxVariableVolatile>
5070 
5071             if (!pn->qualifiedDataType().isEmpty()) {
5072                 writeStartTag(DT_cxxVariableDeclaredType);
5073                 writeCharacters(pn->qualifiedDataType());
5074                 writeEndTag(); // <cxxVariableDeclaredType>
5075             }
5076             QString fq = fullQualification(pn);
5077             if (!fq.isEmpty()) {
5078                 writeStartTag(DT_cxxVariableScopedName);
5079                 writeCharacters(fq);
5080                 writeEndTag(); // <cxxVariableScopedName>
5081             }
5082 
5083             writeStartTag(DT_cxxVariablePrototype);
5084             xmlWriter().writeCharacters("Q_PROPERTY(");
5085             writeCharacters(pn->qualifiedDataType());
5086             xmlWriter().writeCharacters(" ");
5087             writeCharacters(pn->name());
5088             writePropertyParameter("READ",pn->getters());
5089             writePropertyParameter("WRITE",pn->setters());
5090             writePropertyParameter("RESET",pn->resetters());
5091             writePropertyParameter("NOTIFY",pn->notifiers());
5092             if (pn->isDesignable() != pn->designableDefault()) {
5093                 xmlWriter().writeCharacters(" DESIGNABLE ");
5094                 if (!pn->runtimeDesignabilityFunction().isEmpty())
5095                     writeCharacters(pn->runtimeDesignabilityFunction());
5096                 else
5097                     xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false");
5098             }
5099             if (pn->isScriptable() != pn->scriptableDefault()) {
5100                 xmlWriter().writeCharacters(" SCRIPTABLE ");
5101                 if (!pn->runtimeScriptabilityFunction().isEmpty())
5102                     writeCharacters(pn->runtimeScriptabilityFunction());
5103                 else
5104                     xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false");
5105             }
5106             if (pn->isWritable() != pn->writableDefault()) {
5107                 xmlWriter().writeCharacters(" STORED ");
5108                 xmlWriter().writeCharacters(pn->isStored() ? "true" : "false");
5109             }
5110             if (pn->isUser() != pn->userDefault()) {
5111                 xmlWriter().writeCharacters(" USER ");
5112                 xmlWriter().writeCharacters(pn->isUser() ? "true" : "false");
5113             }
5114             if (pn->isConstant())
5115                 xmlWriter().writeCharacters(" CONSTANT");
5116             if (pn->isFinal())
5117                 xmlWriter().writeCharacters(" FINAL");
5118             xmlWriter().writeCharacters(")");
5119             writeEndTag(); // <cxxVariablePrototype>
5120 
5121             writeStartTag(DT_cxxVariableNameLookup);
5122             writeCharacters(pn->parent()->name() + "::" + pn->name());
5123             writeEndTag(); // <cxxVariableNameLookup>
5124 
5125             if (pn->overriddenFrom() != 0) {
5126                 PropertyNode* opn = (PropertyNode*)pn->overriddenFrom();
5127                 writeStartTag(DT_cxxVariableReimplemented);
5128                 xmlWriter().writeAttribute("href",opn->ditaXmlHref());
5129                 writeCharacters(marker->plainFullName(opn));
5130                 writeEndTag(); // </cxxVariableReimplemented>
5131             }
5132 
5133             writeLocation(pn);
5134             writeEndTag(); // <cxxVariableDefinition>
5135 
5136             writeApiDesc(pn, marker, QString());
5137 
5138             // not included: <example> or <apiImpl>
5139 
5140             writeEndTag(); // </cxxVariableDetail>
5141 
5142             // not included: <related-links>
5143 
5144             writeEndTag(); // </cxxVariable>
5145         }
5146         ++m;
5147     }
5148 }
5149 
5150 /*!
5151   This function outputs the nodes resulting from \variable commands.
5152  */
writeDataMembers(const Section & s,CodeMarker * marker,const QString & attribute)5153 void DitaXmlGenerator::writeDataMembers(const Section& s,
5154                                         CodeMarker* marker,
5155                                         const QString& attribute)
5156 {
5157     NodeList::ConstIterator m = s.members.begin();
5158     while (m != s.members.end()) {
5159         if ((*m)->type() == Node::Variable) {
5160             const VariableNode* vn = static_cast<const VariableNode*>(*m);
5161             writeStartTag(DT_cxxVariable);
5162             xmlWriter().writeAttribute("id",vn->guid());
5163             if (!attribute.isEmpty())
5164                 xmlWriter().writeAttribute("outputclass",attribute);
5165             writeStartTag(DT_apiName);
5166             writeCharacters(vn->name());
5167             writeEndTag(); // </apiName>
5168             generateBrief(vn,marker);
5169 
5170             // not included: <prolog>
5171 
5172             writeStartTag(DT_cxxVariableDetail);
5173             writeStartTag(DT_cxxVariableDefinition);
5174             writeStartTag(DT_cxxVariableAccessSpecifier);
5175             xmlWriter().writeAttribute("value",vn->accessString());
5176             writeEndTag(); // <cxxVariableAccessSpecifier>
5177 
5178             // not included: <cxxVAriableStorageClassSpecifierExtern>
5179 
5180             if (vn->isStatic()) {
5181                 writeStartTag(DT_cxxVariableStorageClassSpecifierStatic);
5182                 xmlWriter().writeAttribute("name","static");
5183                 xmlWriter().writeAttribute("value","static");
5184                 writeEndTag(); // <cxxVariableStorageClassSpecifierStatic>
5185             }
5186 
5187             // not included: <cxxVAriableStorageClassSpecifierMutable>,
5188             //               <cxxVariableConst>, <cxxVariableVolatile>
5189 
5190             writeStartTag(DT_cxxVariableDeclaredType);
5191             writeCharacters(vn->leftType());
5192             if (!vn->rightType().isEmpty())
5193                 writeCharacters(vn->rightType());
5194             writeEndTag(); // <cxxVariableDeclaredType>
5195 
5196             QString fq = fullQualification(vn);
5197             if (!fq.isEmpty()) {
5198                 writeStartTag(DT_cxxVariableScopedName);
5199                 writeCharacters(fq);
5200                 writeEndTag(); // <cxxVariableScopedName>
5201             }
5202 
5203             writeStartTag(DT_cxxVariablePrototype);
5204             writeCharacters(vn->leftType() + " ");
5205             //writeCharacters(vn->parent()->name() + "::" + vn->name());
5206             writeCharacters(vn->name());
5207             if (!vn->rightType().isEmpty())
5208                 writeCharacters(vn->rightType());
5209             writeEndTag(); // <cxxVariablePrototype>
5210 
5211             writeStartTag(DT_cxxVariableNameLookup);
5212             writeCharacters(vn->parent()->name() + "::" + vn->name());
5213             writeEndTag(); // <cxxVariableNameLookup>
5214 
5215             // not included: <cxxVariableReimplemented>
5216 
5217             writeLocation(vn);
5218             writeEndTag(); // <cxxVariableDefinition>
5219 
5220             writeApiDesc(vn, marker, QString());
5221 
5222             // not included: <example> or <apiImpl>
5223 
5224             writeEndTag(); // </cxxVariableDetail>
5225 
5226             // not included: <related-links>
5227 
5228             writeEndTag(); // </cxxVariable>
5229         }
5230         ++m;
5231     }
5232 }
5233 
5234 /*!
5235   This function writes a \macro as a <cxxDefine>.
5236  */
writeMacros(const Section & s,CodeMarker * marker,const QString & attribute)5237 void DitaXmlGenerator::writeMacros(const Section& s,
5238                                    CodeMarker* marker,
5239                                    const QString& attribute)
5240 {
5241     NodeList::ConstIterator m = s.members.begin();
5242     while (m != s.members.end()) {
5243         if ((*m)->type() == Node::Function) {
5244             const FunctionNode* fn = static_cast<const FunctionNode*>(*m);
5245             if (fn->isMacro()) {
5246                 writeStartTag(DT_cxxDefine);
5247                 xmlWriter().writeAttribute("id",fn->guid());
5248                 if (!attribute.isEmpty())
5249                     xmlWriter().writeAttribute("outputclass",attribute);
5250                 writeStartTag(DT_apiName);
5251                 writeCharacters(fn->name());
5252                 writeEndTag(); // </apiName>
5253                 generateBrief(fn,marker);
5254 
5255                 // not included: <prolog>
5256 
5257                 writeStartTag(DT_cxxDefineDetail);
5258                 writeStartTag(DT_cxxDefineDefinition);
5259                 writeStartTag(DT_cxxDefineAccessSpecifier);
5260                 xmlWriter().writeAttribute("value",fn->accessString());
5261                 writeEndTag(); // <cxxDefineAccessSpecifier>
5262 
5263                 writeStartTag(DT_cxxDefinePrototype);
5264                 xmlWriter().writeCharacters("#define ");
5265                 writeCharacters(fn->name());
5266                 if (fn->metaness() == FunctionNode::MacroWithParams) {
5267                     QStringList params = fn->parameterNames();
5268                     if (!params.isEmpty()) {
5269                         xmlWriter().writeCharacters("(");
5270                         for (int i = 0; i < params.size(); ++i) {
5271                             if (params[i].isEmpty())
5272                                 xmlWriter().writeCharacters("...");
5273                             else
5274                                 writeCharacters(params[i]);
5275                             if ((i+1) < params.size())
5276                                 xmlWriter().writeCharacters(", ");
5277                         }
5278                         xmlWriter().writeCharacters(")");
5279                     }
5280                 }
5281                 writeEndTag(); // <cxxDefinePrototype>
5282 
5283                 writeStartTag(DT_cxxDefineNameLookup);
5284                 writeCharacters(fn->name());
5285                 writeEndTag(); // <cxxDefineNameLookup>
5286 
5287                 if (fn->reimplementedFrom() != 0) {
5288                     FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom();
5289                     writeStartTag(DT_cxxDefineReimplemented);
5290                     xmlWriter().writeAttribute("href",rfn->ditaXmlHref());
5291                     writeCharacters(marker->plainFullName(rfn));
5292                     writeEndTag(); // </cxxDefineReimplemented>
5293                 }
5294 
5295                 if (fn->metaness() == FunctionNode::MacroWithParams) {
5296                     QStringList params = fn->parameterNames();
5297                     if (!params.isEmpty()) {
5298                         writeStartTag(DT_cxxDefineParameters);
5299                         for (int i = 0; i < params.size(); ++i) {
5300                             writeStartTag(DT_cxxDefineParameter);
5301                             writeStartTag(DT_cxxDefineParameterDeclarationName);
5302                             writeCharacters(params[i]);
5303                             writeEndTag(); // <cxxDefineParameterDeclarationName>
5304 
5305                             // not included: <apiDefNote>
5306 
5307                             writeEndTag(); // <cxxDefineParameter>
5308                         }
5309                         writeEndTag(); // <cxxDefineParameters>
5310                     }
5311                 }
5312 
5313                 writeLocation(fn);
5314                 writeEndTag(); // <cxxDefineDefinition>
5315 
5316                 writeApiDesc(fn, marker, QString());
5317 
5318                 // not included: <example> or <apiImpl>
5319 
5320                 writeEndTag(); // </cxxDefineDetail>
5321 
5322                 // not included: <related-links>
5323 
5324                 writeEndTag(); // </cxxDefine>
5325             }
5326         }
5327         ++m;
5328     }
5329 }
5330 
5331 /*!
5332   This function writes one parameter of a Q_PROPERTY macro.
5333   The property is identified by \a tag ("READ" "WRIE" etc),
5334   and it is found in the 'a nlist.
5335  */
writePropertyParameter(const QString & tag,const NodeList & nlist)5336 void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist)
5337 {
5338     NodeList::const_iterator n = nlist.begin();
5339     while (n != nlist.end()) {
5340         xmlWriter().writeCharacters(" ");
5341         writeCharacters(tag);
5342         xmlWriter().writeCharacters(" ");
5343         writeCharacters((*n)->name());
5344         ++n;
5345     }
5346 }
5347 
5348 /*!
5349   Calls beginSubPage() in the base class to open the file.
5350   Then creates a new XML stream writer using the IO device
5351   from opened file and pushes the XML writer onto a stackj.
5352   Creates the file named \a fileName in the output directory.
5353   Attaches a QTextStream to the created file, which is written
5354   to all over the place using out(). Finally, it sets some
5355   parameters in the XML writer and calls writeStartDocument().
5356  */
beginSubPage(const Location & location,const QString & fileName)5357 void DitaXmlGenerator::beginSubPage(const Location& location,
5358                                     const QString& fileName)
5359 {
5360     PageGenerator::beginSubPage(location,fileName);
5361     (void) lookupGuidMap(fileName);
5362     QXmlStreamWriter* writer = new QXmlStreamWriter(out().device());
5363     xmlWriterStack.push(writer);
5364     writer->setAutoFormatting(true);
5365     writer->setAutoFormattingIndent(4);
5366     writer->writeStartDocument();
5367     clearSectionNesting();
5368 }
5369 
5370 /*!
5371   Calls writeEndDocument() and then pops the XML stream writer
5372   off the stack and deletes it. Then it calls endSubPage() in
5373   the base class to close the device.
5374  */
endSubPage()5375 void DitaXmlGenerator::endSubPage()
5376 {
5377     if (inSection())
5378         qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel;
5379     xmlWriter().writeEndDocument();
5380     delete xmlWriterStack.pop();
5381     PageGenerator::endSubPage();
5382 }
5383 
5384 /*!
5385   Returns a reference to the XML stream writer currently in use.
5386   There is one XML stream writer open for each XML file being
5387   written, and they are kept on a stack. The one on top of the
5388   stack is the one being written to at the moment.
5389  */
xmlWriter()5390 QXmlStreamWriter& DitaXmlGenerator::xmlWriter()
5391 {
5392     return *xmlWriterStack.top();
5393 }
5394 
5395 /*!
5396   Writes the \e {<apiDesc>} element for \a node to the current XML
5397   stream using the code \a marker and the \a title.
5398  */
writeApiDesc(const Node * node,CodeMarker * marker,const QString & title)5399 void DitaXmlGenerator::writeApiDesc(const Node* node,
5400                                     CodeMarker* marker,
5401                                     const QString& title)
5402 {
5403     if (!node->doc().isEmpty()) {
5404         inDetailedDescription = true;
5405         enterApiDesc(QString(),title);
5406         generateBody(node, marker);
5407         generateAlsoList(node, marker);
5408         leaveSection();
5409     }
5410     inDetailedDescription = false;
5411 }
5412 
5413 /*!
5414   Write the nested class elements.
5415  */
writeNestedClasses(const Section & s,const Node * n)5416 void DitaXmlGenerator::writeNestedClasses(const Section& s,
5417                                           const Node* n)
5418 {
5419     if (s.members.isEmpty())
5420         return;
5421     writeStartTag(DT_cxxClassNested);
5422     writeStartTag(DT_cxxClassNestedDetail);
5423 
5424     NodeList::ConstIterator m = s.members.begin();
5425     while (m != s.members.end()) {
5426         if ((*m)->type() == Node::Class) {
5427             writeStartTag(DT_cxxClassNestedClass);
5428             QString link = linkForNode((*m), n);
5429             xmlWriter().writeAttribute("href", link);
5430             QString name = n->name() + "::" + (*m)->name();
5431             writeCharacters(name);
5432             writeEndTag(); // <cxxClassNestedClass>
5433         }
5434         ++m;
5435     }
5436     writeEndTag(); // <cxxClassNestedDetail>
5437     writeEndTag(); // <cxxClassNested>
5438 }
5439 
5440 /*!
5441   Recursive writing of DITA XML files from the root \a node.
5442  */
5443 void
generateInnerNode(const InnerNode * node)5444 DitaXmlGenerator::generateInnerNode(const InnerNode* node)
5445 {
5446     if (!node->url().isNull())
5447         return;
5448 
5449     if (node->type() == Node::Fake) {
5450         const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
5451         if (fakeNode->subType() == Node::ExternalPage)
5452             return;
5453         if (fakeNode->subType() == Node::Image)
5454             return;
5455         if (fakeNode->subType() == Node::QmlPropertyGroup)
5456             return;
5457         if (fakeNode->subType() == Node::Page) {
5458             if (node->count() > 0)
5459                 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
5460         }
5461     }
5462 
5463     /*
5464       Obtain a code marker for the source file.
5465      */
5466     CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
5467 
5468     if (node->parent() != 0) {
5469 	beginSubPage(node->location(), fileName(node));
5470 	if (node->type() == Node::Namespace || node->type() == Node::Class) {
5471 	    generateClassLikeNode(node, marker);
5472 	}
5473         else if (node->type() == Node::Fake) {
5474             if (node->subType() == Node::HeaderFile)
5475                 generateClassLikeNode(node, marker);
5476             else if (node->subType() == Node::QmlClass)
5477                 generateClassLikeNode(node, marker);
5478             else
5479                 generateFakeNode(static_cast<const FakeNode*>(node), marker);
5480 	}
5481 	endSubPage();
5482     }
5483 
5484     NodeList::ConstIterator c = node->childNodes().begin();
5485     while (c != node->childNodes().end()) {
5486 	if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
5487 	    generateInnerNode((const InnerNode*) *c);
5488 	++c;
5489     }
5490 }
5491 
5492 /*!
5493   Returns true if \a format is "XML" or "HTML" .
5494  */
canHandleFormat(const QString & format)5495 bool DitaXmlGenerator::canHandleFormat(const QString& format)
5496 {
5497     return (format == "HTML") || (format == this->format());
5498 }
5499 
writeDitaMap()5500 void DitaXmlGenerator::writeDitaMap()
5501 {
5502     beginSubPage(Location(),"qt-dita-map.xml");
5503 
5504     QString doctype;
5505     doctype = "<!DOCTYPE cxxAPIMap PUBLIC \"-//NOKIA//DTD DITA C++ API Map Reference Type v0.6.0//EN\" \"dtd/cxxAPIMap.dtd\">";
5506 
5507     xmlWriter().writeDTD(doctype);
5508     writeStartTag(DT_cxxAPIMap);
5509     xmlWriter().writeAttribute("id","Qt-DITA-Map");
5510     xmlWriter().writeAttribute("title","Qt DITA Map");
5511     writeStartTag(DT_topicmeta);
5512     writeStartTag(DT_shortdesc);
5513     xmlWriter().writeCharacters("The top level map for the Qt documentation");
5514     writeEndTag(); // </shortdesc>
5515     writeEndTag(); // </topicmeta>
5516     GuidMaps::iterator i = guidMaps.begin();
5517     while (i != guidMaps.end()) {
5518         writeStartTag(DT_topicref);
5519         xmlWriter().writeAttribute("href",i.key());
5520         xmlWriter().writeAttribute("type","topic");
5521         writeEndTag(); // </topicref>
5522         ++i;
5523     }
5524     endSubPage();
5525 }
5526 
5527 /*!
5528   Looks up the tag name for \a t in the map of metadata
5529   values for the current topic in \a inner. If a value
5530   for the tag is found, the element is written with the
5531   found value. Otherwise if \a force is set, an empty
5532   element is written using the tag.
5533 
5534   Returns true or false depending on whether it writes
5535   an element using the tag \a t.
5536 
5537   \note If \a t is found in the metadata map, it is erased.
5538   i.e. Once you call this function for a particular \a t,
5539   you consume \a t.
5540  */
writeMetadataElement(const InnerNode * inner,DitaXmlGenerator::DitaTag t,bool force)5541 bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner,
5542                                             DitaXmlGenerator::DitaTag t,
5543                                             bool force)
5544 {
5545     QString s = getMetadataElement(inner,t);
5546     if (s.isEmpty() && !force)
5547         return false;
5548     writeStartTag(t);
5549     if (!s.isEmpty())
5550         xmlWriter().writeCharacters(s);
5551     writeEndTag();
5552     return true;
5553 }
5554 
5555 
5556 /*!
5557   Looks up the tag name for \a t in the map of metadata
5558   values for the current topic in \a inner. If one or more
5559   value sfor the tag are found, the elements are written.
5560   Otherwise nothing is written.
5561 
5562   Returns true or false depending on whether it writes
5563   at least one element using the tag \a t.
5564 
5565   \note If \a t is found in the metadata map, it is erased.
5566   i.e. Once you call this function for a particular \a t,
5567   you consume \a t.
5568  */
writeMetadataElements(const InnerNode * inner,DitaXmlGenerator::DitaTag t)5569 bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner,
5570                                              DitaXmlGenerator::DitaTag t)
5571 {
5572     QStringList s = getMetadataElements(inner,t);
5573     if (s.isEmpty())
5574         return false;
5575     for (int i=0; i<s.size(); ++i) {
5576         writeStartTag(t);
5577         xmlWriter().writeCharacters(s[i]);
5578         writeEndTag();
5579     }
5580     return true;
5581 }
5582 
5583 /*!
5584   Looks up the tag name for \a t in the map of metadata
5585   values for the current topic in \a inner. If a value
5586   for the tag is found, the value is returned.
5587 
5588   \note If \a t is found in the metadata map, it is erased.
5589   i.e. Once you call this function for a particular \a t,
5590   you consume \a t.
5591  */
getMetadataElement(const InnerNode * inner,DitaXmlGenerator::DitaTag t)5592 QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t)
5593 {
5594     QString s = Generator::getMetadataElement(inner, ditaTags[t]);
5595     if (s.isEmpty())
5596         s = metadataDefault(t);
5597     return s;
5598 }
5599 
5600 /*!
5601   Looks up the tag name for \a t in the map of metadata
5602   values for the current topic in \a inner. If values
5603   for the tag are found, they are returned in a string
5604   list.
5605 
5606   \note If \a t is found in the metadata map, all the
5607   pairs having the key \a t are erased. i.e. Once you
5608   all this function for a particular \a t, you consume
5609   \a t.
5610  */
getMetadataElements(const InnerNode * inner,DitaXmlGenerator::DitaTag t)5611 QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner,
5612                                                   DitaXmlGenerator::DitaTag t)
5613 {
5614     QStringList s = Generator::getMetadataElements(inner,ditaTags[t]);
5615     if (s.isEmpty())
5616         s.append(metadataDefault(t));
5617     return s;
5618 }
5619 
5620 /*!
5621   Returns the value of key \a t or an empty string
5622   if \a t is not found in the map.
5623  */
metadataDefault(DitaTag t) const5624 QString DitaXmlGenerator::metadataDefault(DitaTag t) const
5625 {
5626     return metadataDefaults.value(ditaTags[t]);
5627 }
5628 
5629 /*!
5630   Writes the <prolog> element for the \a inner node
5631   using the \a marker. The <prolog> element contains
5632   the <metadata> element, plus some others. This
5633   function writes one or more of these elements:
5634 
5635   \list
5636     \o <audience> *
5637     \o <author> *
5638     \o <brand> not used
5639     \o <category> *
5640     \o <compomnent> *
5641     \o <copyrholder> *
5642     \o <copyright> *
5643     \o <created> not used
5644     \o <copyryear> *
5645     \o <critdates> not used
5646     \o <keyword> not used
5647     \o <keywords> not used
5648     \o <metadata> *
5649     \o <othermeta> *
5650     \o <permissions> *
5651     \o <platform> not used
5652     \o <prodinfo> *
5653     \o <prodname> *
5654     \o <prolog> *
5655     \o <publisher> *
5656     \o <resourceid> not used
5657     \o <revised> not used
5658     \o <source> not used
5659     \o <tm> not used
5660     \o <unknown> not used
5661     \o <vrm> *
5662     \o <vrmlist> *
5663   \endlist
5664 
5665   \node * means the tag has been used.
5666 
5667  */
5668 void
writeProlog(const InnerNode * inner)5669 DitaXmlGenerator::writeProlog(const InnerNode* inner)
5670 {
5671     if (!inner)
5672         return;
5673     writeStartTag(DT_prolog);
5674     writeMetadataElements(inner,DT_author);
5675     writeMetadataElement(inner,DT_publisher);
5676     QString s = getMetadataElement(inner,DT_copyryear);
5677     QString t = getMetadataElement(inner,DT_copyrholder);
5678     writeStartTag(DT_copyright);
5679     writeStartTag(DT_copyryear);
5680     if (!s.isEmpty())
5681         xmlWriter().writeAttribute("year",s);
5682     writeEndTag(); // </copyryear>
5683     writeStartTag(DT_copyrholder);
5684     if (!s.isEmpty())
5685         xmlWriter().writeCharacters(t);
5686     writeEndTag(); // </copyrholder>
5687     writeEndTag(); // </copyright>
5688     s = getMetadataElement(inner,DT_permissions);
5689     writeStartTag(DT_permissions);
5690     xmlWriter().writeAttribute("view",s);
5691     writeEndTag(); // </permissions>
5692     writeStartTag(DT_metadata);
5693     QStringList sl = getMetadataElements(inner,DT_audience);
5694     if (!sl.isEmpty()) {
5695         for (int i=0; i<sl.size(); ++i) {
5696             writeStartTag(DT_audience);
5697             xmlWriter().writeAttribute("type",sl[i]);
5698             writeEndTag(); // </audience>
5699         }
5700     }
5701     if (!writeMetadataElement(inner,DT_category,false)) {
5702         writeStartTag(DT_category);
5703         QString category = "Page";
5704         if (inner->type() == Node::Class)
5705             category = "Class reference";
5706         else if (inner->type() == Node::Namespace)
5707             category = "Namespace";
5708         else if (inner->type() == Node::Fake) {
5709             if (inner->subType() == Node::QmlClass)
5710                 category = "QML Element Reference";
5711             else if (inner->subType() == Node::QmlBasicType)
5712                 category = "QML Basic Type";
5713             else if (inner->subType() == Node::HeaderFile)
5714                 category = "Header File";
5715             else if (inner->subType() == Node::Module)
5716                 category = "Module";
5717             else if (inner->subType() == Node::File)
5718                 category = "Example Source File";
5719             else if (inner->subType() == Node::Example)
5720                 category = "Example";
5721             else if (inner->subType() == Node::Image)
5722                 category = "Image";
5723             else if (inner->subType() == Node::Group)
5724                 category = "Group";
5725             else if (inner->subType() == Node::Page)
5726                 category = "Page";
5727             else if (inner->subType() == Node::ExternalPage)
5728                 category = "External Page"; // Is this necessary?
5729         }
5730         xmlWriter().writeCharacters(category);
5731         writeEndTag(); // </category>
5732     }
5733     if (vrm.size() > 0) {
5734         writeStartTag(DT_prodinfo);
5735         if (!writeMetadataElement(inner,DT_prodname,false)) {
5736             writeStartTag(DT_prodname);
5737             xmlWriter().writeCharacters(projectDescription);
5738             writeEndTag(); // </prodname>
5739         }
5740         writeStartTag(DT_vrmlist);
5741         writeStartTag(DT_vrm);
5742         if (vrm.size() > 0)
5743             xmlWriter().writeAttribute("version",vrm[0]);
5744         if (vrm.size() > 1)
5745             xmlWriter().writeAttribute("release",vrm[1]);
5746         if (vrm.size() > 2)
5747             xmlWriter().writeAttribute("modification",vrm[2]);
5748         writeEndTag(); // <vrm>
5749         writeEndTag(); // <vrmlist>
5750         if (!writeMetadataElement(inner,DT_component,false)) {
5751             QString component = inner->moduleName();
5752             if (!component.isEmpty()) {
5753                 writeStartTag(DT_component);
5754                 xmlWriter().writeCharacters(component);
5755                 writeEndTag(); // </component>
5756             }
5757         }
5758         writeEndTag(); // </prodinfo>
5759     }
5760     const QStringMultiMap& metaTagMap = inner->doc().metaTagMap();
5761     QMapIterator<QString, QString> i(metaTagMap);
5762     while (i.hasNext()) {
5763         i.next();
5764         writeStartTag(DT_othermeta);
5765         xmlWriter().writeAttribute("name",i.key());
5766         xmlWriter().writeAttribute("content",i.value());
5767         writeEndTag(); // </othermeta>
5768     }
5769     writeEndTag(); // </metadata>
5770     writeEndTag(); // </prolog>
5771 }
5772 
5773 QT_END_NAMESPACE
5774