source: trunk/tools/qdoc3/generator.cpp@ 166

Last change on this file since 166 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 35.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 generator.cpp
44*/
45
46#include <qdir.h>
47
48#include "codemarker.h"
49#include "config.h"
50#include "doc.h"
51#include "editdistance.h"
52#include "generator.h"
53#include "node.h"
54#include "openedlist.h"
55#include "quoter.h"
56#include "separator.h"
57#include "tokenizer.h"
58
59QT_BEGIN_NAMESPACE
60
61QList<Generator *> Generator::generators;
62QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
63QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
64QMap<QString, QStringList> Generator::imgFileExts;
65QSet<QString> Generator::outputFormats;
66QStringList Generator::imageFiles;
67QStringList Generator::imageDirs;
68QString Generator::outDir;
69QString Generator::project;
70
71static Text stockLink(const QString &target)
72{
73 return Text() << Atom(Atom::Link, target) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
74 << target << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
75}
76
77Generator::Generator()
78 : amp("&amp;"), lt("&lt;"), gt("&gt;"), quot("&quot;"), tag("</?@[^>]*>")
79{
80 generators.prepend(this);
81}
82
83Generator::~Generator()
84{
85 generators.removeAll(this);
86}
87
88void Generator::initializeGenerator(const Config & /* config */)
89{
90}
91
92void Generator::terminateGenerator()
93{
94}
95
96void Generator::initialize(const Config &config)
97{
98 outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
99 if (!outputFormats.isEmpty()) {
100 outDir = config.getString(CONFIG_OUTPUTDIR);
101 if (outDir.isEmpty())
102 config.lastLocation().fatal(tr("No output directory specified in configuration file"));
103
104 QDir dirInfo;
105 if (dirInfo.exists(outDir)) {
106 if (!Config::removeDirContents(outDir))
107 config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
108 }
109 else {
110 if (!dirInfo.mkpath(outDir))
111 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
112 }
113
114 if (!dirInfo.mkdir(outDir + "/images"))
115 config.lastLocation().fatal(tr("Cannot create output directory '%1'")
116 .arg(outDir + "/images"));
117 }
118
119 imageFiles = config.getStringList(CONFIG_IMAGES);
120 imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
121
122 QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
123 QSet<QString> formats = config.subVars(imagesDotFileExtensions);
124 QSet<QString>::ConstIterator f = formats.begin();
125 while (f != formats.end()) {
126 imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f);
127 ++f;
128 }
129
130 QList<Generator *>::ConstIterator g = generators.begin();
131 while (g != generators.end()) {
132 if (outputFormats.contains((*g)->format())) {
133 (*g)->initializeGenerator(config);
134 QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES + Config::dot
135 + (*g)->format());
136 QStringList::ConstIterator e = extraImages.begin();
137 while (e != extraImages.end()) {
138 QString userFriendlyFilePath;
139 QString filePath = Config::findFile(config.lastLocation(), imageFiles, imageDirs, *e,
140 imgFileExts[(*g)->format()], userFriendlyFilePath);
141 if (!filePath.isEmpty())
142 Config::copyFile(config.lastLocation(), filePath, userFriendlyFilePath,
143 (*g)->outputDir() + "/images");
144 ++e;
145 }
146 }
147 ++g;
148 }
149
150 QRegExp secondParamAndAbove("[\2-\7]");
151 QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
152 QSet<QString>::ConstIterator n = formattingNames.begin();
153 while (n != formattingNames.end()) {
154 QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
155
156 QSet<QString> formats = config.subVars(formattingDotName);
157 QSet<QString>::ConstIterator f = formats.begin();
158 while (f != formats.end()) {
159 QString def = config.getString(formattingDotName + Config::dot +
160 *f);
161 if (!def.isEmpty()) {
162 int numParams = Config::numParams(def);
163 int numOccs = def.count("\1");
164
165 if (numParams != 1) {
166 config.lastLocation().warning(tr("Formatting '%1' must have exactly one"
167 " parameter (found %2)")
168 .arg(*n).arg(numParams));
169 }
170 else if (numOccs > 1) {
171 config.lastLocation().fatal(tr("Formatting '%1' must contain exactly one"
172 " occurrence of '\\1' (found %2)")
173 .arg(*n).arg(numOccs));
174 }
175 else {
176 int paramPos = def.indexOf("\1");
177 fmtLeftMaps[*f].insert(*n, def.left(paramPos));
178 fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
179 }
180 }
181 ++f;
182 }
183 ++n;
184 }
185
186 project = config.getString(CONFIG_PROJECT);
187}
188
189void Generator::terminate()
190{
191 QList<Generator *>::ConstIterator g = generators.begin();
192 while (g != generators.end()) {
193 if (outputFormats.contains((*g)->format()))
194 (*g)->terminateGenerator();
195 ++g;
196 }
197
198 fmtLeftMaps.clear();
199 fmtRightMaps.clear();
200 imgFileExts.clear();
201 imageFiles.clear();
202 imageDirs.clear();
203 outDir = "";
204}
205
206Generator *Generator::generatorForFormat(const QString& format)
207{
208 QList<Generator *>::ConstIterator g = generators.begin();
209 while (g != generators.end()) {
210 if ((*g)->format() == format)
211 return *g;
212 ++g;
213 }
214 return 0;
215}
216
217void Generator::startText(const Node * /* relative */,
218 CodeMarker * /* marker */)
219{
220}
221
222void Generator::endText(const Node * /* relative */,
223 CodeMarker * /* marker */)
224{
225}
226
227int Generator::generateAtom(const Atom * /* atom */,
228 const Node * /* relative */,
229 CodeMarker * /* marker */)
230{
231 return 0;
232}
233
234void Generator::generateClassLikeNode(const InnerNode * /* classe */,
235 CodeMarker * /* marker */)
236{
237}
238
239void Generator::generateFakeNode(const FakeNode * /* fake */,
240 CodeMarker * /* marker */)
241{
242}
243
244void Generator::generateText(const Text& text,
245 const Node *relative,
246 CodeMarker *marker)
247{
248 if (text.firstAtom() != 0) {
249 int numAtoms = 0;
250 startText(relative, marker);
251 generateAtomList(text.firstAtom(),
252 relative,
253 marker,
254 true,
255 numAtoms);
256 endText(relative, marker);
257 }
258}
259
260#ifdef QDOC_QML
261void Generator::generateQmlText(const Text& text,
262 const Node *relative,
263 CodeMarker *marker)
264{
265 if (text.firstAtom() != 0) {
266 startText(relative, marker);
267 const Atom *atom = text.firstAtom();
268 while (atom) {
269 if (atom->type() != Atom::QmlText)
270 atom = atom->next();
271 else {
272 atom = atom->next();
273 while (atom && (atom->type() != Atom::EndQmlText)) {
274 int n = 1 + generateAtom(atom, relative, marker);
275 while (n-- > 0)
276 atom = atom->next();
277 }
278 }
279 }
280 endText(relative, marker);
281 }
282}
283#endif
284
285void Generator::generateBody(const Node *node, CodeMarker *marker)
286{
287 bool quiet = false;
288
289 if (node->type() == Node::Function) {
290#if 0
291 const FunctionNode *func = (const FunctionNode *) node;
292 if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
293 generateOverload(node, marker);
294#endif
295 }
296 else if (node->type() == Node::Fake) {
297 const FakeNode *fake = static_cast<const FakeNode *>(node);
298 if (fake->subType() == FakeNode::Example)
299 generateExampleFiles(fake, marker);
300 else if (fake->subType() == FakeNode::File)
301 quiet = true;
302 }
303
304 if (node->doc().isEmpty()) {
305 if (!quiet) // ### might be unnecessary
306 node->location().warning(tr("No documentation for '%1'")
307 .arg(marker->plainFullName(node)));
308 }
309 else {
310 generateText(node->doc().body(), node, marker);
311
312 if (node->type() == Node::Enum) {
313 const EnumNode *enume = (const EnumNode *) node;
314
315 QSet<QString> definedItems;
316 QList<EnumItem>::ConstIterator it = enume->items().begin();
317 while (it != enume->items().end()) {
318 definedItems.insert((*it).name());
319 ++it;
320 }
321
322 QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
323 QSet<QString> allItems = definedItems + documentedItems;
324 if (allItems.count() > definedItems.count() ||
325 allItems.count() > documentedItems.count()) {
326 QSet<QString>::ConstIterator a = allItems.begin();
327 while (a != allItems.end()) {
328 if (!definedItems.contains(*a)) {
329 QString details;
330 QString best = nearestName(*a, definedItems);
331 if (!best.isEmpty() && !documentedItems.contains(best))
332 details = tr("Maybe you meant '%1'?").arg(best);
333
334 node->doc().location().warning(
335 tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
336 details);
337 }
338 else if (!documentedItems.contains(*a)) {
339 node->doc().location().warning(
340 tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
341 }
342 ++a;
343 }
344 }
345 }
346 else if (node->type() == Node::Function) {
347 const FunctionNode *func = static_cast<const FunctionNode *>(node);
348
349 QSet<QString> definedParams;
350 QList<Parameter>::ConstIterator p = func->parameters().begin();
351 while (p != func->parameters().end()) {
352 if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
353 && func->name() != QLatin1String("operator++")
354 && func->name() != QLatin1String("operator--")) {
355 node->doc().location().warning(tr("Missing parameter name"));
356 }
357 else {
358 definedParams.insert((*p).name());
359 }
360 ++p;
361 }
362
363 QSet<QString> documentedParams = func->doc().parameterNames();
364 QSet<QString> allParams = definedParams + documentedParams;
365 if (allParams.count() > definedParams.count()
366 || allParams.count() > documentedParams.count()) {
367 QSet<QString>::ConstIterator a = allParams.begin();
368 while (a != allParams.end()) {
369 if (!definedParams.contains(*a)) {
370 QString details;
371 QString best = nearestName(*a, definedParams);
372 if (!best.isEmpty())
373 details = tr("Maybe you meant '%1'?").arg(best);
374
375 node->doc().location().warning(
376 tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
377 details);
378 }
379 else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
380 bool needWarning = (func->status() > Node::Obsolete);
381 if (func->overloadNumber() > 1) {
382 FunctionNode *primaryFunc =
383 func->parent()->findFunctionNode(func->name());
384 if (primaryFunc) {
385 foreach (const Parameter &param, primaryFunc->parameters()) {
386 if (param.name() == *a) {
387 needWarning = false;
388 break;
389 }
390 }
391 }
392 }
393 if (needWarning)
394 node->doc().location().warning(
395 tr("Undocumented parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
396 }
397 ++a;
398 }
399 }
400/* Something like this return value check should be implemented at some point. */
401 if (func->status() > Node::Obsolete && func->returnType() == "bool"
402 && func->reimplementedFrom() == 0 && !func->isOverload()) {
403 QString body = func->doc().body().toString();
404 if (!body.contains("return", Qt::CaseInsensitive))
405 node->doc().location().warning(tr("Undocumented return value"));
406 }
407
408 if (func->reimplementedFrom() != 0)
409 generateReimplementedFrom(func, marker);
410 }
411 }
412
413 if (node->type() == Node::Fake) {
414 const FakeNode *fake = static_cast<const FakeNode *>(node);
415 if (fake->subType() == FakeNode::File) {
416 Text text;
417 Quoter quoter;
418 Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
419 QString code = quoter.quoteTo(fake->location(), "", "");
420 text << Atom(Atom::Code, code);
421 generateText(text, fake, marker);
422 }
423 }
424}
425
426void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
427{
428 QList<Text> alsoList = node->doc().alsoList();
429 supplementAlsoList(node, alsoList);
430
431 if (!alsoList.isEmpty()) {
432 Text text;
433 text << Atom::ParaLeft << "See also ";
434
435 for (int i = 0; i < alsoList.size(); ++i)
436 text << alsoList.at(i) << separator(i, alsoList.size());
437
438 text << Atom::ParaRight;
439 generateText(text, node, marker);
440 }
441}
442
443void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
444{
445 QList<RelatedClass>::ConstIterator r;
446 int index;
447
448 if (!classe->baseClasses().isEmpty()) {
449 Text text;
450 text << Atom::ParaLeft << "Inherits ";
451
452 r = classe->baseClasses().begin();
453 index = 0;
454 while (r != classe->baseClasses().end()) {
455 text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
456 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
457 << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
458 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
459
460 if ((*r).access == Node::Protected) {
461 text << " (protected)";
462 } else if ((*r).access == Node::Private) {
463 text << " (private)";
464 }
465 text << separator(index++, classe->baseClasses().count());
466 ++r;
467 }
468 text << Atom::ParaRight;
469 generateText(text, classe, marker);
470 }
471}
472
473void Generator::generateInheritedBy(const ClassNode *classe,
474 CodeMarker *marker)
475{
476 if (!classe->derivedClasses().isEmpty()) {
477 Text text;
478 text << Atom::ParaLeft << "Inherited by ";
479
480 appendSortedNames(text, classe, classe->derivedClasses(), marker);
481 text << Atom::ParaRight;
482 generateText(text, classe, marker);
483 }
484}
485
486void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
487{
488 if (fake->childNodes().isEmpty())
489 return;
490
491 OpenedList openedList(OpenedList::Bullet);
492
493 Text text;
494 text << Atom::ParaLeft << "Files:" << Atom::ParaRight
495 << Atom(Atom::ListLeft, openedList.styleString());
496 foreach (const Node *child, fake->childNodes()) {
497 QString exampleFile = child->name();
498 openedList.next();
499 text << Atom(Atom::ListItemNumber, openedList.numberString())
500 << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
501 << Atom(Atom::Link, exampleFile)
502 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
503 << exampleFile
504 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
505 << Atom::ParaRight << Atom(Atom::ListItemRight, openedList.styleString());
506 }
507 text << Atom(Atom::ListRight, openedList.styleString());
508 generateText(text, fake, marker);
509}
510
511void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marker)
512{
513 QString module = classe->moduleName();
514 if (!module.isEmpty()) {
515 Text text;
516 if (Tokenizer::isTrue("defined(consoleedition)")
517 && !editionModuleMap["Console"].contains(module)) {
518 text << Atom::ParaLeft
519 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
520 << "This class is not part of the Qt Console Edition."
521 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
522 << Atom::ParaRight;
523 }
524 else if (Tokenizer::isTrue("defined(desktoplightedition)")
525 && !editionModuleMap["DesktopLight"].contains(module)) {
526 text << Atom::ParaLeft
527 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
528 << "This class is not part of the Qt Desktop Light Edition."
529 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
530 << Atom::ParaRight;
531 }
532 else if (module == "Qt3Support" && Tokenizer::isTrue("defined(opensourceedition)")) {
533 text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
534 << "Note to Qt Desktop Light Edition users:"
535 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
536 << " This class is only available in the "
537 << Atom(Atom::AutoLink, "Qt Desktop Edition")
538 << "." << Atom::ParaRight;
539 }
540
541 generateText(text, classe, marker);
542 }
543}
544
545QString Generator::indent(int level, const QString& markedCode)
546{
547 if (level == 0)
548 return markedCode;
549
550 QString t;
551 int column = 0;
552
553 int i = 0;
554 while (i < (int) markedCode.length()) {
555 if (markedCode.at(i) == QLatin1Char('<')) {
556 while (i < (int) markedCode.length()) {
557 t += markedCode.at(i++);
558 if (markedCode.at(i - 1) == QLatin1Char('>'))
559 break;
560 }
561 } else {
562 if (markedCode.at(i) == QLatin1Char('\n')) {
563 column = 0;
564 } else {
565 if (column == 0) {
566 for (int j = 0; j < level; j++)
567 t += QLatin1Char(' ');
568 }
569 column++;
570 }
571 t += markedCode.at(i++);
572 }
573 }
574 return t;
575}
576
577QString Generator::plainCode(const QString& markedCode)
578{
579 QString t = markedCode;
580 t.replace(tag, QString());
581 t.replace(quot, QLatin1String("\""));
582 t.replace(gt, QLatin1String(">"));
583 t.replace(lt, QLatin1String("<"));
584 t.replace(amp, QLatin1String("&"));
585 return t;
586}
587
588QString Generator::typeString(const Node *node)
589{
590 switch (node->type()) {
591 case Node::Namespace:
592 return "namespace";
593 case Node::Class:
594 return "class";
595 case Node::Fake:
596 default:
597 return "documentation";
598 case Node::Enum:
599 return "enum";
600 case Node::Typedef:
601 return "typedef";
602 case Node::Function:
603 return "function";
604 case Node::Property:
605 return "property";
606 }
607}
608
609QString Generator::imageFileName(const Node *relative, const QString& fileBase)
610{
611 QString userFriendlyFilePath;
612 QString filePath = Config::findFile(
613 relative->doc().location(), imageFiles, imageDirs, fileBase,
614 imgFileExts[format()], userFriendlyFilePath);
615
616 if (filePath.isEmpty())
617 return QString();
618
619 return QLatin1String("images/")
620 + Config::copyFile(relative->doc().location(),
621 filePath, userFriendlyFilePath,
622 outputDir() + QLatin1String("/images"));
623}
624
625void Generator::setImageFileExtensions(const QStringList& extensions)
626{
627 imgFileExts[format()] = extensions;
628}
629
630void Generator::unknownAtom(const Atom *atom)
631{
632 Location::internalError(tr("unknown atom type '%1' in %2 generator")
633 .arg(atom->typeString()).arg(format()));
634}
635
636bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
637{
638 return atom->next() != 0 && atom->next()->type() == expectedAtomType;
639}
640
641void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
642{
643 if (node->type() == Node::Function) {
644 const FunctionNode *func = static_cast<const FunctionNode *>(node);
645 if (func->overloadNumber() == 1) {
646 QString alternateName;
647 const FunctionNode *alternateFunc = 0;
648
649 if (func->name().startsWith("set") && func->name().size() >= 4) {
650 alternateName = func->name()[3].toLower();
651 alternateName += func->name().mid(4);
652 alternateFunc = func->parent()->findFunctionNode(alternateName);
653
654 if (!alternateFunc) {
655 alternateName = "is" + func->name().mid(3);
656 alternateFunc = func->parent()->findFunctionNode(alternateName);
657 if (!alternateFunc) {
658 alternateName = "has" + func->name().mid(3);
659 alternateFunc = func->parent()->findFunctionNode(alternateName);
660 }
661 }
662 } else if (!func->name().isEmpty()) {
663 alternateName = "set";
664 alternateName += func->name()[0].toUpper();
665 alternateName += func->name().mid(1);
666 alternateFunc = func->parent()->findFunctionNode(alternateName);
667 }
668
669 if (alternateFunc && alternateFunc->access() != Node::Private) {
670 int i;
671 for (i = 0; i < alsoList.size(); ++i) {
672 if (alsoList.at(i).toString().contains(alternateName))
673 break;
674 }
675
676 if (i == alsoList.size()) {
677 alternateName += "()";
678
679 Text also;
680 also << Atom(Atom::Link, alternateName)
681 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
682 << alternateName
683 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
684 alsoList.prepend(also);
685 }
686 }
687 }
688 }
689}
690
691QMap<QString, QString>& Generator::formattingLeftMap()
692{
693 return fmtLeftMaps[format()];
694}
695
696QMap<QString, QString>& Generator::formattingRightMap()
697{
698 return fmtRightMaps[format()];
699}
700
701QString Generator::trimmedTrailing(const QString &string)
702{
703 QString trimmed = string;
704 while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
705 trimmed.truncate(trimmed.length() - 1);
706 return trimmed;
707}
708
709void Generator::generateStatus(const Node *node, CodeMarker *marker)
710{
711 Text text;
712
713 switch (node->status()) {
714 case Node::Commendable:
715 case Node::Main:
716 break;
717 case Node::Preliminary:
718 text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
719 << typeString(node) << " is under development and is subject to change."
720 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight;
721 break;
722 case Node::Deprecated:
723 text << Atom::ParaLeft;
724 if (node->isInnerNode())
725 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
726 text << "This " << typeString(node) << " is deprecated.";
727 if (node->isInnerNode())
728 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
729 text << Atom::ParaRight;
730 break;
731 case Node::Obsolete:
732 text << Atom::ParaLeft;
733 if (node->isInnerNode())
734 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
735 text << "This " << typeString(node) << " is obsolete.";
736 if (node->isInnerNode())
737 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
738 text << " It is provided to keep old source code working. We strongly advise against "
739 << "using it in new code." << Atom::ParaRight;
740 break;
741 case Node::Compat:
742 // reimplemented in HtmlGenerator subclass
743 if (node->isInnerNode()) {
744 text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
745 << typeString(node) << " is part of the Qt 3 compatibility layer."
746 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
747 << " It is provided to keep old source code working. We strongly advise against "
748 << "using it in new code. See "
749 << Atom(Atom::AutoLink, "Porting to Qt 4")
750 << " for more information."
751 << Atom::ParaRight;
752 }
753 break;
754 case Node::Internal:
755 default:
756 break;
757 }
758 generateText(text, node, marker);
759}
760
761void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
762{
763 Text text;
764 Text theStockLink;
765 Node::ThreadSafeness parent = node->parent()->inheritedThreadSafeness();
766
767 switch (node->threadSafeness()) {
768 case Node::UnspecifiedSafeness:
769 break;
770 case Node::NonReentrant:
771 text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "Warning:"
772 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " This "
773 << typeString(node) << " is not " << stockLink("reentrant") << "." << Atom::ParaRight;
774 break;
775 case Node::Reentrant:
776 case Node::ThreadSafe:
777 text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
778 if (parent == Node::ThreadSafe) {
779 text << "Warning:";
780 } else {
781 text << "Note:";
782 }
783 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " ";
784
785 if (node->threadSafeness() == Node::ThreadSafe)
786 theStockLink = stockLink("thread-safe");
787 else
788 theStockLink = stockLink("reentrant");
789
790 if (node->isInnerNode()) {
791 const InnerNode *innerNode = static_cast<const InnerNode *>(node);
792 text << "All the functions in this " << typeString(node) << " are "
793 << theStockLink;
794
795 NodeList except;
796 NodeList::ConstIterator c = innerNode->childNodes().begin();
797 while (c != innerNode->childNodes().end()) {
798 if ((*c)->threadSafeness() != Node::UnspecifiedSafeness)
799 except.append(*c);
800 ++c;
801 }
802 if (except.isEmpty()) {
803 text << ".";
804 }
805 else {
806 text << ", except ";
807
808 NodeList::ConstIterator e = except.begin();
809 int index = 0;
810 while (e != except.end()) {
811 appendFullName(text, *e, innerNode, marker);
812 text << separator(index++, except.count());
813 ++e;
814 }
815 }
816 }
817 else {
818 text << "This " << typeString(node) << " is " << theStockLink << ".";
819 }
820 text << Atom::ParaRight;
821 }
822 generateText(text, node, marker);
823}
824
825void Generator::generateSince(const Node *node, CodeMarker *marker)
826{
827 if (!node->since().isEmpty()) {
828 Text text;
829 text << Atom::ParaLeft << "This " << typeString(node)
830 << " was introduced in ";
831 if (project.isEmpty())
832 text << "version";
833 else
834 text << project;
835 text << " " << node->since() << "." << Atom::ParaRight;
836 generateText(text, node, marker);
837 }
838}
839
840/*!
841 No longer in use.
842 */
843void Generator::generateOverload(const Node *node, CodeMarker *marker)
844{
845 Text text;
846 text << Atom::ParaLeft
847 << "This function overloads ";
848 QString t = node->name() + "()";
849 text << Atom::AutoLink << t
850 << Atom::ParaRight;
851 generateText(text, node, marker);
852}
853
854void Generator::generateReimplementedFrom(const FunctionNode *func,
855 CodeMarker *marker)
856{
857 if (func->reimplementedFrom() != 0) {
858 const FunctionNode *from = func->reimplementedFrom();
859 if (from->access() != Node::Private && from->parent()->access() != Node::Private) {
860 Text text;
861 text << Atom::ParaLeft << "Reimplemented from ";
862 appendFullName(text, from->parent(), func, marker, from);
863 text << "." << Atom::ParaRight;
864 generateText(text, func, marker);
865 }
866 }
867}
868
869const Atom *Generator::generateAtomList(const Atom *atom,
870 const Node *relative,
871 CodeMarker *marker,
872 bool generate,
873 int &numAtoms)
874{
875 while (atom) {
876 if (atom->type() == Atom::FormatIf) {
877 int numAtoms0 = numAtoms;
878 bool rightFormat = canHandleFormat(atom->string());
879 atom = generateAtomList(atom->next(),
880 relative,
881 marker,
882 generate && rightFormat,
883 numAtoms);
884 if (!atom)
885 return 0;
886
887 if (atom->type() == Atom::FormatElse) {
888 ++numAtoms;
889 atom = generateAtomList(atom->next(),
890 relative,
891 marker,
892 generate && !rightFormat,
893 numAtoms);
894 if (!atom)
895 return 0;
896 }
897
898 if (atom->type() == Atom::FormatEndif) {
899 if (generate && numAtoms0 == numAtoms) {
900 relative->location().warning(tr("Output format %1 not handled").
901 arg(format()));
902 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
903 generateAtomList(&unhandledFormatAtom,
904 relative,
905 marker,
906 generate,
907 numAtoms);
908 }
909 atom = atom->next();
910 }
911 }
912 else if (atom->type() == Atom::FormatElse || atom->type() == Atom::FormatEndif) {
913 return atom;
914 }
915 else {
916 int n = 1;
917 if (generate) {
918 n += generateAtom(atom, relative, marker);
919 numAtoms += n;
920 }
921 while (n-- > 0)
922 atom = atom->next();
923 }
924 }
925 return 0;
926}
927
928void Generator::appendFullName(Text& text,
929 const Node *apparentNode,
930 const Node *relative,
931 CodeMarker *marker,
932 const Node *actualNode)
933{
934 if (actualNode == 0)
935 actualNode = apparentNode;
936 text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
937 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
938 << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
939 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
940}
941
942void Generator::appendSortedNames(Text& text,
943 const ClassNode *classe,
944 const QList<RelatedClass> &classes,
945 CodeMarker *marker)
946{
947 QList<RelatedClass>::ConstIterator r;
948 QMap<QString,Text> classMap;
949 int index = 0;
950
951 r = classes.begin();
952 while (r != classes.end()) {
953 if ((*r).node->access() == Node::Public && (*r).node->status() != Node::Internal
954 && !(*r).node->doc().isEmpty()) {
955 Text className;
956 appendFullName(className, (*r).node, classe, marker);
957 classMap[className.toString().toLower()] = className;
958 }
959 ++r;
960 }
961
962 QStringList classNames = classMap.keys();
963 classNames.sort();
964
965 foreach (const QString &className, classNames) {
966 text << classMap[className];
967 text << separator(index++, classNames.count());
968 }
969}
970
971int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
972{
973 int skipAhead = 0;
974 atom = atom->next();
975 while (atom != 0 && atom->type() != type) {
976 skipAhead++;
977 atom = atom->next();
978 }
979 return skipAhead;
980}
981
982QString Generator::fullName(const Node *node,
983 const Node *relative,
984 CodeMarker *marker) const
985{
986 if (node->type() == Node::Fake)
987 return static_cast<const FakeNode *>(node)->title();
988 else if (node->type() == Node::Class &&
989 !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
990 return (static_cast<const ClassNode *>(node))->serviceName();
991 else
992 return marker->plainFullName(node, relative);
993}
994
995QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.