Merge "Merge remote-tracking branch 'origin/master' into api_changes" into refs/stagi...
[profile/ivi/qtbase.git] / src / tools / qdoc / generator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   generator.cpp
44 */
45 #include <qdir.h>
46 #ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
47 #include <qdebug.h>
48 #endif
49 #include <qdebug.h>
50 #include "codemarker.h"
51 #include "config.h"
52 #include "doc.h"
53 #include "editdistance.h"
54 #include "generator.h"
55 #include "node.h"
56 #include "openedlist.h"
57 #include "quoter.h"
58 #include "separator.h"
59 #include "tokenizer.h"
60 #include "ditaxmlgenerator.h"
61
62 QT_BEGIN_NAMESPACE
63
64 QList<Generator *> Generator::generators;
65 QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
66 QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
67 QMap<QString, QStringList> Generator::imgFileExts;
68 QSet<QString> Generator::outputFormats;
69 QStringList Generator::imageFiles;
70 QStringList Generator::imageDirs;
71 QStringList Generator::exampleDirs;
72 QStringList Generator::exampleImgExts;
73 QStringList Generator::scriptFiles;
74 QStringList Generator::scriptDirs;
75 QStringList Generator::styleFiles;
76 QStringList Generator::styleDirs;
77 QString Generator::outDir_;
78 QString Generator::baseDir_;
79 QString Generator::project;
80 QHash<QString, QString> Generator::outputPrefixes;
81
82 QString Generator::sinceTitles[] =
83 {
84     "    New Namespaces",
85     "    New Classes",
86     "    New Member Functions",
87     "    New Functions in Namespaces",
88     "    New Global Functions",
89     "    New Macros",
90     "    New Enum Types",
91     "    New Typedefs",
92     "    New Properties",
93     "    New Variables",
94     "    New QML Elements",
95     "    New QML Properties",
96     "    New QML Signals",
97     "    New QML Signal Handlers",
98     "    New QML Methods",
99     ""
100 };
101
102 static void singularPlural(Text& text, const NodeList& nodes)
103 {
104     if (nodes.count() == 1)
105         text << " is";
106     else
107         text << " are";
108 }
109
110 Generator::Generator()
111     : amp("&amp;"),
112       lt("&lt;"),
113       gt("&gt;"),
114       quot("&quot;"),
115       tag("</?@[^>]*>")
116 {
117     generators.prepend(this);
118 }
119
120 Generator::~Generator()
121 {
122     generators.removeAll(this);
123 }
124
125 void Generator::initializeGenerator(const Config & /* config */)
126 {
127 }
128
129 void Generator::terminateGenerator()
130 {
131 }
132
133 void Generator::initialize(const Config &config)
134 {
135     outputFormats = config.getOutputFormats();
136     if (!outputFormats.isEmpty()) {
137         outDir_ = config.getOutputDir();
138         baseDir_ = config.getString(CONFIG_BASEDIR);
139         if (!baseDir_.isEmpty())
140             config.location().warning(tr("\"basedir\" specified in config file. "
141                                          "All output will be in module directories of the output directory"));
142         if (outDir_.isEmpty())
143             config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line"));
144
145         QDir dirInfo;
146         if (dirInfo.exists(outDir_)) {
147             if (!Config::removeDirContents(outDir_))
148                 config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_));
149         }
150         else {
151             if (!dirInfo.mkpath(outDir_))
152                 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_));
153         }
154
155         if (!dirInfo.mkdir(outDir_ + "/images"))
156             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
157                                         .arg(outDir_ + "/images"));
158         if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples"))
159             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
160                                         .arg(outDir_ + "/images/used-in-examples"));
161         if (!dirInfo.mkdir(outDir_ + "/scripts"))
162             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
163                                         .arg(outDir_ + "/scripts"));
164         if (!dirInfo.mkdir(outDir_ + "/style"))
165             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
166                                         .arg(outDir_ + "/style"));
167     }
168
169     imageFiles = config.getCleanPathList(CONFIG_IMAGES);
170     imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS);
171     scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS);
172     scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS);
173     styleFiles = config.getCleanPathList(CONFIG_STYLES);
174     styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS);
175     exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
176     exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot +
177                                           CONFIG_IMAGEEXTENSIONS);
178
179     QString imagesDotFileExtensions =
180             CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
181     QSet<QString> formats = config.subVars(imagesDotFileExtensions);
182     QSet<QString>::ConstIterator f = formats.begin();
183     while (f != formats.end()) {
184         imgFileExts[*f] = config.getStringList(imagesDotFileExtensions +
185                                                Config::dot + *f);
186         ++f;
187     }
188
189     QList<Generator *>::ConstIterator g = generators.begin();
190     while (g != generators.end()) {
191         if (outputFormats.contains((*g)->format())) {
192             (*g)->initializeGenerator(config);
193             QStringList extraImages =
194                     config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format());
195             QStringList::ConstIterator e = extraImages.begin();
196             while (e != extraImages.end()) {
197                 QString userFriendlyFilePath;
198                 QString filePath = Config::findFile(config.lastLocation(),
199                                                     imageFiles,
200                                                     imageDirs,
201                                                     *e,
202                                                     imgFileExts[(*g)->format()],
203                                                     userFriendlyFilePath);
204                 if (!filePath.isEmpty())
205                     Config::copyFile(config.lastLocation(),
206                                      filePath,
207                                      userFriendlyFilePath,
208                                      (*g)->outputDir() +
209                                      "/images");
210                 ++e;
211             }
212
213             // Documentation template handling
214             QString templateDir = config.getString(
215                         (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR);
216
217             if (!templateDir.isEmpty()) {
218                 QStringList noExts;
219                 QStringList searchDirs = QStringList() << templateDir;
220                 QStringList scripts =
221                         config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS);
222                 e = scripts.begin();
223                 while (e != scripts.end()) {
224                     QString userFriendlyFilePath;
225                     QString filePath = Config::findFile(config.lastLocation(),
226                                                         scriptFiles,
227                                                         searchDirs,
228                                                         *e,
229                                                         noExts,
230                                                         userFriendlyFilePath);
231                     if (!filePath.isEmpty())
232                         Config::copyFile(config.lastLocation(),
233                                          filePath,
234                                          userFriendlyFilePath,
235                                          (*g)->outputDir() +
236                                          "/scripts");
237                     ++e;
238                 }
239
240                 QStringList styles =
241                         config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS);
242                 e = styles.begin();
243                 while (e != styles.end()) {
244                     QString userFriendlyFilePath;
245                     QString filePath = Config::findFile(config.lastLocation(),
246                                                         styleFiles,
247                                                         searchDirs,
248                                                         *e,
249                                                         noExts,
250                                                         userFriendlyFilePath);
251                     if (!filePath.isEmpty())
252                         Config::copyFile(config.lastLocation(),
253                                          filePath,
254                                          userFriendlyFilePath,
255                                          (*g)->outputDir() +
256                                          "/style");
257                     ++e;
258                 }
259             }
260         }
261         ++g;
262     }
263
264     QRegExp secondParamAndAbove("[\2-\7]");
265     QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
266     QSet<QString>::ConstIterator n = formattingNames.begin();
267     while (n != formattingNames.end()) {
268         QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
269
270         QSet<QString> formats = config.subVars(formattingDotName);
271         QSet<QString>::ConstIterator f = formats.begin();
272         while (f != formats.end()) {
273             QString def = config.getString(formattingDotName +
274                                            Config::dot + *f);
275             if (!def.isEmpty()) {
276                 int numParams = Config::numParams(def);
277                 int numOccs = def.count("\1");
278
279                 if (numParams != 1) {
280                     config.lastLocation().warning(tr("Formatting '%1' must "
281                                                      "have exactly one "
282                                                      "parameter (found %2)")
283                                                   .arg(*n).arg(numParams));
284                 }
285                 else if (numOccs > 1) {
286                     config.lastLocation().fatal(tr("Formatting '%1' must "
287                                                    "contain exactly one "
288                                                    "occurrence of '\\1' "
289                                                    "(found %2)")
290                                                 .arg(*n).arg(numOccs));
291                 }
292                 else {
293                     int paramPos = def.indexOf("\1");
294                     fmtLeftMaps[*f].insert(*n, def.left(paramPos));
295                     fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
296                 }
297             }
298             ++f;
299         }
300         ++n;
301     }
302
303     project = config.getString(CONFIG_PROJECT);
304
305     QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES);
306     if (!prefixes.isEmpty()) {
307         foreach (QString prefix, prefixes)
308             outputPrefixes[prefix] = config.getString(
309                         CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
310     } else
311         outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
312 }
313
314 void Generator::terminate()
315 {
316     QList<Generator *>::ConstIterator g = generators.begin();
317     while (g != generators.end()) {
318         if (outputFormats.contains((*g)->format()))
319             (*g)->terminateGenerator();
320         ++g;
321     }
322
323     fmtLeftMaps.clear();
324     fmtRightMaps.clear();
325     imgFileExts.clear();
326     imageFiles.clear();
327     imageDirs.clear();
328     outDir_ = "";
329     QmlClassNode::terminate();
330     ExampleNode::terminate();
331 }
332
333 Generator *Generator::generatorForFormat(const QString& format)
334 {
335     QList<Generator *>::ConstIterator g = generators.begin();
336     while (g != generators.end()) {
337         if ((*g)->format() == format)
338             return *g;
339         ++g;
340     }
341     return 0;
342 }
343
344 void Generator::startText(const Node * /* relative */,
345                           CodeMarker * /* marker */)
346 {
347 }
348
349 void Generator::endText(const Node * /* relative */,
350                         CodeMarker * /* marker */)
351 {
352 }
353
354 int Generator::generateAtom(const Atom * /* atom */,
355                             const Node * /* relative */,
356                             CodeMarker * /* marker */)
357 {
358     return 0;
359 }
360
361 void Generator::generateClassLikeNode(const InnerNode * /* classe */,
362                                       CodeMarker * /* marker */)
363 {
364 }
365
366 void Generator::generateFakeNode(const FakeNode * /* fake */,
367                                  CodeMarker * /* marker */)
368 {
369 }
370
371 bool Generator::generateText(const Text& text,
372                              const Node *relative,
373                              CodeMarker *marker)
374 {
375     bool result = false;
376     if (text.firstAtom() != 0) {
377         int numAtoms = 0;
378         startText(relative, marker);
379         generateAtomList(text.firstAtom(),
380                          relative,
381                          marker,
382                          true,
383                          numAtoms);
384         endText(relative, marker);
385         result = true;
386     }
387     return result;
388 }
389
390 #ifdef QDOC_QML
391 /*!
392   Extract sections of markup text surrounded by \e qmltext
393   and \e endqmltext and output them.
394  */
395 bool Generator::generateQmlText(const Text& text,
396                                 const Node *relative,
397                                 CodeMarker *marker,
398                                 const QString& /* qmlName */ )
399 {
400     const Atom* atom = text.firstAtom();
401     bool result = false;
402
403     if (atom != 0) {
404         startText(relative, marker);
405         while (atom) {
406             if (atom->type() != Atom::QmlText)
407                 atom = atom->next();
408             else {
409                 atom = atom->next();
410                 while (atom && (atom->type() != Atom::EndQmlText)) {
411                     int n = 1 + generateAtom(atom, relative, marker);
412                     while (n-- > 0)
413                         atom = atom->next();
414                 }
415             }
416         }
417         endText(relative, marker);
418         result = true;
419     }
420     return result;
421 }
422 #endif
423
424 void Generator::generateBody(const Node *node, CodeMarker *marker)
425 {
426     bool quiet = false;
427
428     if (node->type() == Node::Fake) {
429         const FakeNode *fake = static_cast<const FakeNode *>(node);
430         if (fake->subType() == Node::Example) {
431             generateExampleFiles(fake, marker);
432         }
433         else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image)) {
434             quiet = true;
435         }
436     }
437     if (node->doc().isEmpty()) {
438         if (!quiet && !node->isReimp()) { // ### might be unnecessary
439             node->location().warning(tr("No documentation for '%1'")
440                                      .arg(marker->plainFullName(node)));
441         }
442     }
443     else {
444         if (node->type() == Node::Function) {
445             const FunctionNode *func = static_cast<const FunctionNode *>(node);
446             if (func->reimplementedFrom() != 0)
447                 generateReimplementedFrom(func, marker);
448         }
449
450         if (!generateText(node->doc().body(), node, marker)) {
451             if (node->isReimp())
452                 return;
453         }
454
455         if (node->type() == Node::Enum) {
456             const EnumNode *enume = (const EnumNode *) node;
457
458             QSet<QString> definedItems;
459             QList<EnumItem>::ConstIterator it = enume->items().begin();
460             while (it != enume->items().end()) {
461                 definedItems.insert((*it).name());
462                 ++it;
463             }
464
465             QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
466             QSet<QString> allItems = definedItems + documentedItems;
467             if (allItems.count() > definedItems.count() ||
468                     allItems.count() > documentedItems.count()) {
469                 QSet<QString>::ConstIterator a = allItems.begin();
470                 while (a != allItems.end()) {
471                     if (!definedItems.contains(*a)) {
472                         QString details;
473                         QString best = nearestName(*a, definedItems);
474                         if (!best.isEmpty() && !documentedItems.contains(best))
475                             details = tr("Maybe you meant '%1'?").arg(best);
476
477                         node->doc().location().warning(
478                                     tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
479                                     details);
480                     }
481                     else if (!documentedItems.contains(*a)) {
482                         node->doc().location().warning(
483                                     tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
484                     }
485                     ++a;
486                 }
487             }
488         }
489         else if (node->type() == Node::Function) {
490             const FunctionNode *func = static_cast<const FunctionNode *>(node);
491             QSet<QString> definedParams;
492             QList<Parameter>::ConstIterator p = func->parameters().begin();
493             while (p != func->parameters().end()) {
494                 if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
495                         && func->name() != QLatin1String("operator++")
496                         && func->name() != QLatin1String("operator--")) {
497                     node->doc().location().warning(tr("Missing parameter name"));
498                 }
499                 else {
500                     definedParams.insert((*p).name());
501                 }
502                 ++p;
503             }
504
505             QSet<QString> documentedParams = func->doc().parameterNames();
506             QSet<QString> allParams = definedParams + documentedParams;
507             if (allParams.count() > definedParams.count()
508                     || allParams.count() > documentedParams.count()) {
509                 QSet<QString>::ConstIterator a = allParams.begin();
510                 while (a != allParams.end()) {
511                     if (!definedParams.contains(*a)) {
512                         QString details;
513                         QString best = nearestName(*a, definedParams);
514                         if (!best.isEmpty())
515                             details = tr("Maybe you meant '%1'?").arg(best);
516
517                         node->doc().location().warning(
518                                     tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
519                                     details);
520                     }
521                     else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
522                         bool needWarning = (func->status() > Node::Obsolete);
523                         if (func->overloadNumber() > 1) {
524                             FunctionNode *primaryFunc =
525                                     func->parent()->findFunctionNode(func->name());
526                             if (primaryFunc) {
527                                 foreach (const Parameter &param,
528                                          primaryFunc->parameters()) {
529                                     if (param.name() == *a) {
530                                         needWarning = false;
531                                         break;
532                                     }
533                                 }
534                             }
535                         }
536                         if (needWarning && !func->isReimp())
537                             node->doc().location().warning(
538                                         tr("Undocumented parameter '%1' in %2")
539                                         .arg(*a).arg(marker->plainFullName(node)));
540                     }
541                     ++a;
542                 }
543             }
544             /*
545               Something like this return value check should
546               be implemented at some point.
547             */
548             if (func->status() > Node::Obsolete && func->returnType() == "bool"
549                     && func->reimplementedFrom() == 0 && !func->isOverload()) {
550                 QString body = func->doc().body().toString();
551                 if (!body.contains("return", Qt::CaseInsensitive))
552                     node->doc().location().warning(tr("Undocumented return value"));
553             }
554         }
555     }
556
557     if (node->type() == Node::Fake) {
558         const FakeNode *fake = static_cast<const FakeNode *>(node);
559         if (fake->subType() == Node::File) {
560             Text text;
561             Quoter quoter;
562             Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
563             QString code = quoter.quoteTo(fake->location(), "", "");
564             CodeMarker *codeMarker = CodeMarker::markerForFileName(fake->name());
565             text << Atom(codeMarker->atomType(), code);
566             generateText(text, fake, codeMarker);
567         }
568     }
569 }
570
571 void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
572 {
573     QList<Text> alsoList = node->doc().alsoList();
574     supplementAlsoList(node, alsoList);
575
576     if (!alsoList.isEmpty()) {
577         Text text;
578         text << Atom::ParaLeft
579              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
580              << "See also "
581              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
582
583         for (int i = 0; i < alsoList.size(); ++i)
584             text << alsoList.at(i) << separator(i, alsoList.size());
585
586         text << Atom::ParaRight;
587         generateText(text, node, marker);
588     }
589 }
590
591 /*!
592   Generate a list of maintainers in the output
593  */
594 void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker)
595 {
596     QStringList sl = getMetadataElements(node,"maintainer");
597
598     if (!sl.isEmpty()) {
599         Text text;
600         text << Atom::ParaLeft
601              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
602              << "Maintained by: "
603              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
604
605         for (int i = 0; i < sl.size(); ++i)
606             text << sl.at(i) << separator(i, sl.size());
607
608         text << Atom::ParaRight;
609         generateText(text, node, marker);
610     }
611 }
612
613 void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
614 {
615     QList<RelatedClass>::ConstIterator r;
616     int index;
617
618     if (!classe->baseClasses().isEmpty()) {
619         Text text;
620         text << Atom::ParaLeft
621              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
622              << "Inherits: "
623              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
624
625         r = classe->baseClasses().begin();
626         index = 0;
627         while (r != classe->baseClasses().end()) {
628             text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
629                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
630                  << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
631                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
632
633             if ((*r).access == Node::Protected) {
634                 text << " (protected)";
635             }
636             else if ((*r).access == Node::Private) {
637                 text << " (private)";
638             }
639             text << separator(index++, classe->baseClasses().count());
640             ++r;
641         }
642         text << Atom::ParaRight;
643         generateText(text, classe, marker);
644     }
645 }
646
647 #ifdef QDOC_QML
648 /*!
649  */
650 void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
651 {
652     // stub.
653 }
654 #endif
655
656 void Generator::generateInheritedBy(const ClassNode *classe,
657                                     CodeMarker *marker)
658 {
659     if (!classe->derivedClasses().isEmpty()) {
660         Text text;
661         text << Atom::ParaLeft
662              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
663              << "Inherited by: "
664              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
665
666         appendSortedNames(text, classe, classe->derivedClasses(), marker);
667         text << Atom::ParaRight;
668         generateText(text, classe, marker);
669     }
670 }
671
672 /*!
673   This function is called when the documentation for an
674   example is being formatted. It outputs the list of source
675   files comprising the example, and the list of images used
676   by the example. The images are copied into a subtree of
677   \c{...doc/html/images/used-in-examples/...}
678  */
679 void Generator::generateFileList(const FakeNode* fake,
680                                  CodeMarker* marker,
681                                  Node::SubType subtype,
682                                  const QString& tag)
683 {
684     int count = 0;
685     Text text;
686     OpenedList openedList(OpenedList::Bullet);
687
688     text << Atom::ParaLeft << tag << Atom::ParaRight
689          << Atom(Atom::ListLeft, openedList.styleString());
690
691     foreach (const Node* child, fake->childNodes()) {
692         if (child->subType() == subtype) {
693             ++count;
694             QString file = child->name();
695             if (subtype == Node::Image) {
696                 if (!file.isEmpty()) {
697                     QDir dirInfo;
698                     QString userFriendlyFilePath;
699                     QString srcPath = Config::findFile(fake->location(),
700                                                        QStringList(),
701                                                        exampleDirs,
702                                                        file,
703                                                        exampleImgExts,
704                                                        userFriendlyFilePath);
705                     userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
706
707                     QString imgOutDir = outDir_ + "/images/used-in-examples/" + userFriendlyFilePath;
708                     if (!dirInfo.mkpath(imgOutDir))
709                         fake->location().fatal(tr("Cannot create output directory '%1'")
710                                                .arg(imgOutDir));
711
712                     QString imgOutName = Config::copyFile(fake->location(),
713                                                           srcPath,
714                                                           file,
715                                                           imgOutDir);
716                 }
717
718             }
719
720             openedList.next();
721             text << Atom(Atom::ListItemNumber, openedList.numberString())
722                  << Atom(Atom::ListItemLeft, openedList.styleString())
723                  << Atom::ParaLeft
724                  << Atom(Atom::Link, file)
725                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
726                  << file
727                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
728                  << Atom::ParaRight
729                  << Atom(Atom::ListItemRight, openedList.styleString());
730         }
731     }
732     text << Atom(Atom::ListRight, openedList.styleString());
733     if (count > 0)
734         generateText(text, fake, marker);
735 }
736
737 void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
738 {
739     if (fake->childNodes().isEmpty())
740         return;
741     generateFileList(fake, marker, Node::File, QString("Files:"));
742     generateFileList(fake, marker, Node::Image, QString("Images:"));
743 }
744
745 QString Generator::indent(int level, const QString& markedCode)
746 {
747     if (level == 0)
748         return markedCode;
749
750     QString t;
751     int column = 0;
752
753     int i = 0;
754     while (i < (int) markedCode.length()) {
755         if (markedCode.at(i) == QLatin1Char('\n')) {
756             column = 0;
757         }
758         else {
759             if (column == 0) {
760                 for (int j = 0; j < level; j++)
761                     t += QLatin1Char(' ');
762             }
763             column++;
764         }
765         t += markedCode.at(i++);
766     }
767     return t;
768 }
769
770 QString Generator::plainCode(const QString& markedCode)
771 {
772     QString t = markedCode;
773     t.replace(tag, QString());
774     t.replace(quot, QLatin1String("\""));
775     t.replace(gt, QLatin1String(">"));
776     t.replace(lt, QLatin1String("<"));
777     t.replace(amp, QLatin1String("&"));
778     return t;
779 }
780
781 QString Generator::typeString(const Node *node)
782 {
783     switch (node->type()) {
784     case Node::Namespace:
785         return "namespace";
786     case Node::Class:
787         return "class";
788     case Node::Fake:
789     {
790         switch (node->subType()) {
791         case Node::QmlClass:
792             return "element";
793         case Node::QmlPropertyGroup:
794             return "property group";
795         case Node::QmlBasicType:
796             return "type";
797         default:
798             return "documentation";
799         }
800     }
801     case Node::Enum:
802         return "enum";
803     case Node::Typedef:
804         return "typedef";
805     case Node::Function:
806         return "function";
807     case Node::Property:
808         return "property";
809     default:
810         return "documentation";
811     }
812 }
813
814 /*!
815   Returns a relative path name for an image.
816  */
817 QString Generator::imageFileName(const Node *relative, const QString& fileBase)
818 {
819     QString userFriendlyFilePath;
820     QString filePath = Config::findFile(
821                 relative->doc().location(), imageFiles, imageDirs, fileBase,
822                 imgFileExts[format()], userFriendlyFilePath);
823
824     if (filePath.isEmpty())
825         return QString();
826
827     QString path = Config::copyFile(relative->doc().location(),
828                                     filePath,
829                                     userFriendlyFilePath,
830                                     outputDir() + QLatin1String("/images"));
831     QString images = "images";
832     if (path[0] != '/')
833         images.append(QLatin1Char('/'));
834     return images + path;
835 }
836
837 void Generator::setImageFileExtensions(const QStringList& extensions)
838 {
839     imgFileExts[format()] = extensions;
840 }
841
842 void Generator::unknownAtom(const Atom *atom)
843 {
844     Location::internalError(tr("unknown atom type '%1' in %2 generator")
845                             .arg(atom->typeString()).arg(format()));
846 }
847
848 bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
849 {
850     return atom->next() != 0 && atom->next()->type() == expectedAtomType;
851 }
852
853 void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
854 {
855     if (node->type() == Node::Function) {
856         const FunctionNode *func = static_cast<const FunctionNode *>(node);
857         if (func->overloadNumber() == 1) {
858             QString alternateName;
859             const FunctionNode *alternateFunc = 0;
860
861             if (func->name().startsWith("set") && func->name().size() >= 4) {
862                 alternateName = func->name()[3].toLower();
863                 alternateName += func->name().mid(4);
864                 alternateFunc = func->parent()->findFunctionNode(alternateName);
865
866                 if (!alternateFunc) {
867                     alternateName = "is" + func->name().mid(3);
868                     alternateFunc = func->parent()->findFunctionNode(alternateName);
869                     if (!alternateFunc) {
870                         alternateName = "has" + func->name().mid(3);
871                         alternateFunc = func->parent()->findFunctionNode(alternateName);
872                     }
873                 }
874             }
875             else if (!func->name().isEmpty()) {
876                 alternateName = "set";
877                 alternateName += func->name()[0].toUpper();
878                 alternateName += func->name().mid(1);
879                 alternateFunc = func->parent()->findFunctionNode(alternateName);
880             }
881
882             if (alternateFunc && alternateFunc->access() != Node::Private) {
883                 int i;
884                 for (i = 0; i < alsoList.size(); ++i) {
885                     if (alsoList.at(i).toString().contains(alternateName))
886                         break;
887                 }
888
889                 if (i == alsoList.size()) {
890                     alternateName += "()";
891
892                     Text also;
893                     also << Atom(Atom::Link, alternateName)
894                          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
895                          << alternateName
896                          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
897                     alsoList.prepend(also);
898                 }
899             }
900         }
901     }
902 }
903
904 QMap<QString, QString>& Generator::formattingLeftMap()
905 {
906     return fmtLeftMaps[format()];
907 }
908
909 QMap<QString, QString>& Generator::formattingRightMap()
910 {
911     return fmtRightMaps[format()];
912 }
913
914 /*
915   Trims trailimng whitespace off the \a string and returns
916   the trimmed string.
917  */
918 QString Generator::trimmedTrailing(const QString& string)
919 {
920     QString trimmed = string;
921     while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
922         trimmed.truncate(trimmed.length() - 1);
923     return trimmed;
924 }
925
926 void Generator::generateStatus(const Node *node, CodeMarker *marker)
927 {
928     Text text;
929
930     switch (node->status()) {
931     case Node::Commendable:
932     case Node::Main:
933         break;
934     case Node::Preliminary:
935         text << Atom::ParaLeft
936              << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
937              << "This "
938              << typeString(node)
939              << " is under development and is subject to change."
940              << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
941              << Atom::ParaRight;
942         break;
943     case Node::Deprecated:
944         text << Atom::ParaLeft;
945         if (node->isInnerNode())
946             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
947         text << "This " << typeString(node) << " is deprecated.";
948         if (node->isInnerNode())
949             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
950         text << Atom::ParaRight;
951         break;
952     case Node::Obsolete:
953         text << Atom::ParaLeft;
954         if (node->isInnerNode())
955             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
956         text << "This " << typeString(node) << " is obsolete.";
957         if (node->isInnerNode())
958             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
959         text << " It is provided to keep old source code working. "
960              << "We strongly advise against "
961              << "using it in new code." << Atom::ParaRight;
962         break;
963     case Node::Compat:
964         // reimplemented in HtmlGenerator subclass
965         if (node->isInnerNode()) {
966             text << Atom::ParaLeft
967                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
968                  << "This "
969                  << typeString(node)
970                  << " is part of the Qt 3 compatibility layer."
971                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
972                  << " It is provided to keep old source code working. "
973                  << "We strongly advise against "
974                  << "using it in new code. See "
975                  << Atom(Atom::AutoLink, "Porting to Qt 4")
976                  << " for more information."
977                  << Atom::ParaRight;
978         }
979         break;
980     case Node::Internal:
981     default:
982         break;
983     }
984     generateText(text, node, marker);
985 }
986
987 void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
988 {
989     Text text;
990     Text theStockLink;
991     Node::ThreadSafeness threadSafeness = node->threadSafeness();
992
993     Text rlink;
994     rlink << Atom(Atom::Link,"reentrant")
995           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
996           << "reentrant"
997           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
998
999     Text tlink;
1000     tlink << Atom(Atom::Link,"thread-safe")
1001           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1002           << "thread-safe"
1003           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1004
1005     switch (threadSafeness) {
1006     case Node::UnspecifiedSafeness:
1007         break;
1008     case Node::NonReentrant:
1009         text << Atom::ParaLeft
1010              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
1011              << "Warning:"
1012              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
1013              << " This "
1014              << typeString(node)
1015              << " is not "
1016              << rlink
1017              << "."
1018              << Atom::ParaRight;
1019         break;
1020     case Node::Reentrant:
1021     case Node::ThreadSafe:
1022         text << Atom::ParaLeft
1023              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
1024              << "Note:"
1025              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
1026              << " ";
1027
1028         if (node->isInnerNode()) {
1029             const InnerNode* innerNode = static_cast<const InnerNode*>(node);
1030             text << "All functions in this "
1031                  << typeString(node)
1032                  << " are ";
1033             if (threadSafeness == Node::ThreadSafe)
1034                 text << tlink;
1035             else
1036                 text << rlink;
1037
1038             bool exceptions = false;
1039             NodeList reentrant;
1040             NodeList threadsafe;
1041             NodeList nonreentrant;
1042             NodeList::ConstIterator c = innerNode->childNodes().begin();
1043             while (c != innerNode->childNodes().end()) {
1044
1045                 if ((*c)->status() != Node::Obsolete){
1046                     switch ((*c)->threadSafeness()) {
1047                     case Node::Reentrant:
1048                         reentrant.append(*c);
1049                         if (threadSafeness == Node::ThreadSafe)
1050                             exceptions = true;
1051                         break;
1052                     case Node::ThreadSafe:
1053                         threadsafe.append(*c);
1054                         if (threadSafeness == Node::Reentrant)
1055                             exceptions = true;
1056                         break;
1057                     case Node::NonReentrant:
1058                         nonreentrant.append(*c);
1059                         exceptions = true;
1060                         break;
1061                     default:
1062                         break;
1063                     }
1064                 }
1065                 ++c;
1066             }
1067             if (!exceptions)
1068                 text << ".";
1069             else if (threadSafeness == Node::Reentrant) {
1070                 if (nonreentrant.isEmpty()) {
1071                     if (!threadsafe.isEmpty()) {
1072                         text << ", but ";
1073                         appendFullNames(text,threadsafe,innerNode,marker);
1074                         singularPlural(text,threadsafe);
1075                         text << " also " << tlink << ".";
1076                     }
1077                     else
1078                         text << ".";
1079                 }
1080                 else {
1081                     text << ", except for ";
1082                     appendFullNames(text,nonreentrant,innerNode,marker);
1083                     text << ", which";
1084                     singularPlural(text,nonreentrant);
1085                     text << " nonreentrant.";
1086                     if (!threadsafe.isEmpty()) {
1087                         text << " ";
1088                         appendFullNames(text,threadsafe,innerNode,marker);
1089                         singularPlural(text,threadsafe);
1090                         text << " " << tlink << ".";
1091                     }
1092                 }
1093             }
1094             else { // thread-safe
1095                 if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) {
1096                     text << ", except for ";
1097                     if (!reentrant.isEmpty()) {
1098                         appendFullNames(text,reentrant,innerNode,marker);
1099                         text << ", which";
1100                         singularPlural(text,reentrant);
1101                         text << " only " << rlink;
1102                         if (!nonreentrant.isEmpty())
1103                             text << ", and ";
1104                     }
1105                     if (!nonreentrant.isEmpty()) {
1106                         appendFullNames(text,nonreentrant,innerNode,marker);
1107                         text << ", which";
1108                         singularPlural(text,nonreentrant);
1109                         text << " nonreentrant.";
1110                     }
1111                     text << ".";
1112                 }
1113             }
1114         }
1115         else {
1116             text << "This " << typeString(node) << " is ";
1117             if (threadSafeness == Node::ThreadSafe)
1118                 text << tlink;
1119             else
1120                 text << rlink;
1121             text << ".";
1122         }
1123         text << Atom::ParaRight;
1124     }
1125     generateText(text,node,marker);
1126 }
1127
1128 void Generator::generateSince(const Node *node, CodeMarker *marker)
1129 {
1130     if (!node->since().isEmpty()) {
1131         Text text;
1132         text << Atom::ParaLeft
1133              << "This "
1134              << typeString(node);
1135         if (node->type() == Node::Enum)
1136             text << " was introduced or modified in ";
1137         else
1138             text << " was introduced in ";
1139
1140         QStringList since = node->since().split(QLatin1Char(' '));
1141         if (since.count() == 1) {
1142             // Handle legacy use of \since <version>.
1143             if (project.isEmpty())
1144                 text << "version";
1145             else
1146                 text << project;
1147             text << " " << since[0];
1148         } else {
1149             // Reconstruct the <project> <version> string.
1150             text << " " << since.join(" ");
1151         }
1152
1153         text << "." << Atom::ParaRight;
1154         generateText(text, node, marker);
1155     }
1156 }
1157
1158 void Generator::generateReimplementedFrom(const FunctionNode *func,
1159                                           CodeMarker *marker)
1160 {
1161     if (func->reimplementedFrom() != 0) {
1162         const FunctionNode *from = func->reimplementedFrom();
1163         if (from->access() != Node::Private &&
1164                 from->parent()->access() != Node::Private) {
1165             Text text;
1166             text << Atom::ParaLeft << "Reimplemented from ";
1167             QString fullName =  from->parent()->name() + "::" + from->name() + "()";
1168             appendFullName(text, from->parent(), fullName, from);
1169             text << "." << Atom::ParaRight;
1170             generateText(text, func, marker);
1171         }
1172     }
1173 }
1174
1175 const Atom *Generator::generateAtomList(const Atom *atom,
1176                                         const Node *relative,
1177                                         CodeMarker *marker,
1178                                         bool generate,
1179                                         int &numAtoms)
1180 {
1181     while (atom) {
1182         if (atom->type() == Atom::FormatIf) {
1183             int numAtoms0 = numAtoms;
1184             bool rightFormat = canHandleFormat(atom->string());
1185             atom = generateAtomList(atom->next(),
1186                                     relative,
1187                                     marker,
1188                                     generate && rightFormat,
1189                                     numAtoms);
1190             if (!atom)
1191                 return 0;
1192
1193             if (atom->type() == Atom::FormatElse) {
1194                 ++numAtoms;
1195                 atom = generateAtomList(atom->next(),
1196                                         relative,
1197                                         marker,
1198                                         generate && !rightFormat,
1199                                         numAtoms);
1200                 if (!atom)
1201                     return 0;
1202             }
1203
1204             if (atom->type() == Atom::FormatEndif) {
1205                 if (generate && numAtoms0 == numAtoms) {
1206                     relative->location().warning(tr("Output format %1 not handled %2")
1207                                                  .arg(format()).arg(outFileName()));
1208                     Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
1209                     generateAtomList(&unhandledFormatAtom,
1210                                      relative,
1211                                      marker,
1212                                      generate,
1213                                      numAtoms);
1214                 }
1215                 atom = atom->next();
1216             }
1217         }
1218         else if (atom->type() == Atom::FormatElse ||
1219                  atom->type() == Atom::FormatEndif) {
1220             return atom;
1221         }
1222         else {
1223             int n = 1;
1224             if (generate) {
1225                 n += generateAtom(atom, relative, marker);
1226                 numAtoms += n;
1227             }
1228             while (n-- > 0)
1229                 atom = atom->next();
1230         }
1231     }
1232     return 0;
1233 }
1234
1235 void Generator::appendFullName(Text& text,
1236                                const Node *apparentNode,
1237                                const Node *relative,
1238                                CodeMarker *marker,
1239                                const Node *actualNode)
1240 {
1241     if (actualNode == 0)
1242         actualNode = apparentNode;
1243     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
1244          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1245          << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
1246          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1247 }
1248
1249 void Generator::appendFullName(Text& text,
1250                                const Node *apparentNode,
1251                                const QString& fullName,
1252                                const Node *actualNode)
1253 {
1254     if (actualNode == 0)
1255         actualNode = apparentNode;
1256     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
1257          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1258          << Atom(Atom::String, fullName)
1259          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1260 }
1261
1262 void Generator::appendFullNames(Text& text,
1263                                 const NodeList& nodes,
1264                                 const Node* relative,
1265                                 CodeMarker* marker)
1266 {
1267     NodeList::ConstIterator n = nodes.begin();
1268     int index = 0;
1269     while (n != nodes.end()) {
1270         appendFullName(text,*n,relative,marker);
1271         text << comma(index++,nodes.count());
1272         ++n;
1273     }
1274 }
1275
1276 void Generator::appendSortedNames(Text& text,
1277                                   const ClassNode *classe,
1278                                   const QList<RelatedClass> &classes,
1279                                   CodeMarker *marker)
1280 {
1281     QList<RelatedClass>::ConstIterator r;
1282     QMap<QString,Text> classMap;
1283     int index = 0;
1284
1285     r = classes.begin();
1286     while (r != classes.end()) {
1287         if ((*r).node->access() == Node::Public &&
1288                 (*r).node->status() != Node::Internal
1289                 && !(*r).node->doc().isEmpty()) {
1290             Text className;
1291             appendFullName(className, (*r).node, classe, marker);
1292             classMap[className.toString().toLower()] = className;
1293         }
1294         ++r;
1295     }
1296
1297     QStringList classNames = classMap.keys();
1298     classNames.sort();
1299
1300     foreach (const QString &className, classNames) {
1301         text << classMap[className];
1302         text << separator(index++, classNames.count());
1303     }
1304 }
1305
1306 void Generator::appendSortedQmlNames(Text& text,
1307                                      const Node* base,
1308                                      const NodeList& subs,
1309                                      CodeMarker *marker)
1310 {
1311     QMap<QString,Text> classMap;
1312     int index = 0;
1313
1314     for (int i = 0; i < subs.size(); ++i) {
1315         Text t;
1316         if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() ||
1317                 (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) {
1318             appendFullName(t, subs[i], base, marker);
1319             classMap[t.toString().toLower()] = t;
1320         }
1321     }
1322
1323     QStringList names = classMap.keys();
1324     names.sort();
1325
1326     foreach (const QString &name, names) {
1327         text << classMap[name];
1328         text << separator(index++, names.count());
1329     }
1330 }
1331
1332 int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
1333 {
1334     int skipAhead = 0;
1335     atom = atom->next();
1336     while (atom != 0 && atom->type() != type) {
1337         skipAhead++;
1338         atom = atom->next();
1339     }
1340     return skipAhead;
1341 }
1342
1343 QString Generator::fullName(const Node *node,
1344                             const Node *relative,
1345                             CodeMarker *marker) const
1346 {
1347     if (node->type() == Node::Fake) {
1348         const FakeNode* fn = static_cast<const FakeNode *>(node);
1349 #if 0
1350         // Removed for QTBUG-22870
1351         if (!fn->qmlModuleIdentifier().isEmpty())
1352             return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title();
1353 #endif
1354         return fn->title();
1355     }
1356     else if (node->type() == Node::Class &&
1357              !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
1358         return (static_cast<const ClassNode *>(node))->serviceName();
1359     else
1360         return marker->plainFullName(node, relative);
1361 }
1362
1363 QString Generator::outputPrefix(const QString &nodeType)
1364 {
1365     return outputPrefixes[nodeType];
1366 }
1367
1368 /*!
1369   Looks up the tag \a t in the map of metadata values for the
1370   current topic in \a inner. If a value for the tag is found,
1371   the value is returned.
1372
1373   \note If \a t is found in the metadata map, it is erased.
1374   i.e. Once you call this function for a particular \a t,
1375   you consume \a t.
1376  */
1377 QString Generator::getMetadataElement(const InnerNode* inner, const QString& t)
1378 {
1379     QString s;
1380     QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
1381     QStringMultiMap::iterator i = metaTagMap.find(t);
1382     if (i != metaTagMap.end()) {
1383         s = i.value();
1384         metaTagMap.erase(i);
1385     }
1386     return s;
1387 }
1388
1389 /*!
1390   Looks up the tag \a t in the map of metadata values for the
1391   current topic in \a inner. If values for the tag are found,
1392   they are returned in a string list.
1393
1394   \note If \a t is found in the metadata map, all the pairs
1395   having the key \a t are erased. i.e. Once you call this
1396   function for a particular \a t, you consume \a t.
1397  */
1398 QStringList Generator::getMetadataElements(const InnerNode* inner, const QString& t)
1399 {
1400     QStringList s;
1401     QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
1402     s = metaTagMap.values(t);
1403     if (!s.isEmpty())
1404         metaTagMap.remove(t);
1405     return s;
1406 }
1407
1408 /*!
1409   For generating the "New Classes... in 4.6" section on the
1410   What's New in 4.6" page.
1411  */
1412 void Generator::findAllSince(const InnerNode *node)
1413 {
1414     NodeList::const_iterator child = node->childNodes().constBegin();
1415
1416     // Traverse the tree, starting at the node supplied.
1417
1418     while (child != node->childNodes().constEnd()) {
1419
1420         QString sinceString = (*child)->since();
1421
1422         if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
1423
1424             // Insert a new entry into each map for each new since string found.
1425             NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString);
1426             if (nsmap == newSinceMaps.end())
1427                 nsmap = newSinceMaps.insert(sinceString,NodeMultiMap());
1428
1429             NewClassMaps::iterator ncmap = newClassMaps.find(sinceString);
1430             if (ncmap == newClassMaps.end())
1431                 ncmap = newClassMaps.insert(sinceString,NodeMap());
1432
1433             NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString);
1434             if (nqcmap == newQmlClassMaps.end())
1435                 nqcmap = newQmlClassMaps.insert(sinceString,NodeMap());
1436
1437             if ((*child)->type() == Node::Function) {
1438                 // Insert functions into the general since map.
1439                 FunctionNode *func = static_cast<FunctionNode *>(*child);
1440                 if ((func->status() > Node::Obsolete) &&
1441                         (func->metaness() != FunctionNode::Ctor) &&
1442                         (func->metaness() != FunctionNode::Dtor)) {
1443                     nsmap.value().insert(func->name(),(*child));
1444                 }
1445             }
1446             else if ((*child)->url().isEmpty()) {
1447                 if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
1448                     // Insert classes into the since and class maps.
1449                     QString className = (*child)->name();
1450                     if ((*child)->parent() &&
1451                             (*child)->parent()->type() == Node::Namespace &&
1452                             !(*child)->parent()->name().isEmpty())
1453                         className = (*child)->parent()->name()+"::"+className;
1454
1455                     nsmap.value().insert(className,(*child));
1456                     ncmap.value().insert(className,(*child));
1457                 }
1458                 else if ((*child)->subType() == Node::QmlClass) {
1459                     // Insert QML elements into the since and element maps.
1460                     QString className = (*child)->name();
1461                     if ((*child)->parent() &&
1462                             (*child)->parent()->type() == Node::Namespace &&
1463                             !(*child)->parent()->name().isEmpty())
1464                         className = (*child)->parent()->name()+"::"+className;
1465
1466                     nsmap.value().insert(className,(*child));
1467                     nqcmap.value().insert(className,(*child));
1468                 }
1469                 else if ((*child)->type() == Node::QmlProperty) {
1470                     // Insert QML properties into the since map.
1471                     QString propertyName = (*child)->name();
1472                     nsmap.value().insert(propertyName,(*child));
1473                 }
1474             }
1475             else {
1476                 // Insert external documents into the general since map.
1477                 QString name = (*child)->name();
1478                 if ((*child)->parent() &&
1479                         (*child)->parent()->type() == Node::Namespace &&
1480                         !(*child)->parent()->name().isEmpty())
1481                     name = (*child)->parent()->name()+"::"+name;
1482
1483                 nsmap.value().insert(name,(*child));
1484             }
1485
1486             // Find child nodes with since commands.
1487             if ((*child)->isInnerNode()) {
1488                 findAllSince(static_cast<InnerNode *>(*child));
1489             }
1490         }
1491         ++child;
1492     }
1493 }
1494
1495 QT_END_NAMESPACE