Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / tools / qdoc3 / generator.cpp
blob9b58d7f3d306917c3c888d26f19d46301a07118b
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
43 generator.cpp
45 #include <QtCore>
46 #include <qdir.h>
47 #include <qdebug.h>
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"
59 QT_BEGIN_NAMESPACE
61 QList<Generator *> Generator::generators;
62 QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
63 QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
64 QMap<QString, QStringList> Generator::imgFileExts;
65 QSet<QString> Generator::outputFormats;
66 QStringList Generator::imageFiles;
67 QStringList Generator::imageDirs;
68 QStringList Generator::exampleDirs;
69 QStringList Generator::exampleImgExts;
70 QString Generator::outDir;
71 QString Generator::project;
73 static void singularPlural(Text& text, const NodeList& nodes)
75 if (nodes.count() == 1)
76 text << " is";
77 else
78 text << " are";
81 Generator::Generator()
82 : amp("&amp;"),
83 lt("&lt;"),
84 gt("&gt;"),
85 quot("&quot;"),
86 tag("</?@[^>]*>")
88 generators.prepend(this);
91 Generator::~Generator()
93 generators.removeAll(this);
96 void Generator::initializeGenerator(const Config & /* config */)
100 void Generator::terminateGenerator()
104 void Generator::initialize(const Config &config)
106 outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
107 if (!outputFormats.isEmpty()) {
108 outDir = config.getString(CONFIG_OUTPUTDIR);
109 if (outDir.isEmpty())
110 config.lastLocation().fatal(tr("No output directory specified in configuration file"));
112 QDir dirInfo;
113 if (dirInfo.exists(outDir)) {
114 if (!Config::removeDirContents(outDir))
115 config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
117 else {
118 if (!dirInfo.mkpath(outDir))
119 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
122 if (!dirInfo.mkdir(outDir + "/images"))
123 config.lastLocation().fatal(tr("Cannot create output directory '%1'")
124 .arg(outDir + "/images"));
125 if (!dirInfo.mkdir(outDir + "/images/used-in-examples"))
126 config.lastLocation().fatal(tr("Cannot create output directory '%1'")
127 .arg(outDir + "/images/used-in-examples"));
130 imageFiles = config.getStringList(CONFIG_IMAGES);
131 imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
132 exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
133 exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot +
134 CONFIG_IMAGEEXTENSIONS);
136 QString imagesDotFileExtensions =
137 CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
138 QSet<QString> formats = config.subVars(imagesDotFileExtensions);
139 QSet<QString>::ConstIterator f = formats.begin();
140 while (f != formats.end()) {
141 imgFileExts[*f] = config.getStringList(imagesDotFileExtensions +
142 Config::dot + *f);
143 ++f;
146 QList<Generator *>::ConstIterator g = generators.begin();
147 while (g != generators.end()) {
148 if (outputFormats.contains((*g)->format())) {
149 (*g)->initializeGenerator(config);
150 QStringList extraImages =
151 config.getStringList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format());
152 QStringList::ConstIterator e = extraImages.begin();
153 while (e != extraImages.end()) {
154 QString userFriendlyFilePath;
155 QString filePath = Config::findFile(config.lastLocation(),
156 imageFiles,
157 imageDirs,
159 imgFileExts[(*g)->format()],
160 userFriendlyFilePath);
161 if (!filePath.isEmpty())
162 Config::copyFile(config.lastLocation(),
163 filePath,
164 userFriendlyFilePath,
165 (*g)->outputDir() +
166 "/images");
167 ++e;
170 ++g;
173 QRegExp secondParamAndAbove("[\2-\7]");
174 QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
175 QSet<QString>::ConstIterator n = formattingNames.begin();
176 while (n != formattingNames.end()) {
177 QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
179 QSet<QString> formats = config.subVars(formattingDotName);
180 QSet<QString>::ConstIterator f = formats.begin();
181 while (f != formats.end()) {
182 QString def = config.getString(formattingDotName +
183 Config::dot + *f);
184 if (!def.isEmpty()) {
185 int numParams = Config::numParams(def);
186 int numOccs = def.count("\1");
188 if (numParams != 1) {
189 config.lastLocation().warning(tr("Formatting '%1' must "
190 "have exactly one "
191 "parameter (found %2)")
192 .arg(*n).arg(numParams));
194 else if (numOccs > 1) {
195 config.lastLocation().fatal(tr("Formatting '%1' must "
196 "contain exactly one "
197 "occurrence of '\\1' "
198 "(found %2)")
199 .arg(*n).arg(numOccs));
201 else {
202 int paramPos = def.indexOf("\1");
203 fmtLeftMaps[*f].insert(*n, def.left(paramPos));
204 fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
207 ++f;
209 ++n;
212 project = config.getString(CONFIG_PROJECT);
215 void Generator::terminate()
217 QList<Generator *>::ConstIterator g = generators.begin();
218 while (g != generators.end()) {
219 if (outputFormats.contains((*g)->format()))
220 (*g)->terminateGenerator();
221 ++g;
224 fmtLeftMaps.clear();
225 fmtRightMaps.clear();
226 imgFileExts.clear();
227 imageFiles.clear();
228 imageDirs.clear();
229 outDir = "";
232 Generator *Generator::generatorForFormat(const QString& format)
234 QList<Generator *>::ConstIterator g = generators.begin();
235 while (g != generators.end()) {
236 if ((*g)->format() == format)
237 return *g;
238 ++g;
240 return 0;
243 void Generator::startText(const Node * /* relative */,
244 CodeMarker * /* marker */)
248 void Generator::endText(const Node * /* relative */,
249 CodeMarker * /* marker */)
253 int Generator::generateAtom(const Atom * /* atom */,
254 const Node * /* relative */,
255 CodeMarker * /* marker */)
257 return 0;
260 void Generator::generateClassLikeNode(const InnerNode * /* classe */,
261 CodeMarker * /* marker */)
265 void Generator::generateFakeNode(const FakeNode * /* fake */,
266 CodeMarker * /* marker */)
270 bool Generator::generateText(const Text& text,
271 const Node *relative,
272 CodeMarker *marker)
274 if (text.firstAtom() != 0) {
275 int numAtoms = 0;
276 startText(relative, marker);
277 generateAtomList(text.firstAtom(),
278 relative,
279 marker,
280 true,
281 numAtoms);
282 endText(relative, marker);
283 return true;
285 return false;
288 #ifdef QDOC_QML
290 Extract sections of markup text surrounded by \e qmltext
291 and \e endqmltext and output them.
293 bool Generator::generateQmlText(const Text& text,
294 const Node *relative,
295 CodeMarker *marker,
296 const QString& /* qmlName */ )
298 const Atom* atom = text.firstAtom();
299 if (atom == 0)
300 return false;
302 startText(relative, marker);
303 while (atom) {
304 if (atom->type() != Atom::QmlText)
305 atom = atom->next();
306 else {
307 atom = atom->next();
308 while (atom && (atom->type() != Atom::EndQmlText)) {
309 int n = 1 + generateAtom(atom, relative, marker);
310 while (n-- > 0)
311 atom = atom->next();
315 endText(relative, marker);
316 return true;
318 #endif
320 void Generator::generateBody(const Node *node, CodeMarker *marker)
322 bool quiet = false;
324 if (node->type() == Node::Function) {
325 #if 0
326 const FunctionNode *func = (const FunctionNode *) node;
327 if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
328 generateOverload(node, marker);
329 #endif
331 else if (node->type() == Node::Fake) {
332 const FakeNode *fake = static_cast<const FakeNode *>(node);
333 if (fake->subType() == Node::Example)
334 generateExampleFiles(fake, marker);
335 else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image))
336 quiet = true;
339 if (node->doc().isEmpty()) {
340 if (!quiet && !node->isReimp()) // ### might be unnecessary
341 node->location().warning(tr("No documentation for '%1'")
342 .arg(marker->plainFullName(node)));
344 else {
345 if (node->type() == Node::Function) {
346 const FunctionNode *func = static_cast<const FunctionNode *>(node);
347 if (func->reimplementedFrom() != 0)
348 generateReimplementedFrom(func, marker);
351 if (!generateText(node->doc().body(), node, marker))
352 if (node->isReimp())
353 return;
355 if (node->type() == Node::Enum) {
356 const EnumNode *enume = (const EnumNode *) node;
358 QSet<QString> definedItems;
359 QList<EnumItem>::ConstIterator it = enume->items().begin();
360 while (it != enume->items().end()) {
361 definedItems.insert((*it).name());
362 ++it;
365 QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
366 QSet<QString> allItems = definedItems + documentedItems;
367 if (allItems.count() > definedItems.count() ||
368 allItems.count() > documentedItems.count()) {
369 QSet<QString>::ConstIterator a = allItems.begin();
370 while (a != allItems.end()) {
371 if (!definedItems.contains(*a)) {
372 QString details;
373 QString best = nearestName(*a, definedItems);
374 if (!best.isEmpty() && !documentedItems.contains(best))
375 details = tr("Maybe you meant '%1'?").arg(best);
377 node->doc().location().warning(
378 tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
379 details);
381 else if (!documentedItems.contains(*a)) {
382 node->doc().location().warning(
383 tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
385 ++a;
389 else if (node->type() == Node::Function) {
390 const FunctionNode *func = static_cast<const FunctionNode *>(node);
391 QSet<QString> definedParams;
392 QList<Parameter>::ConstIterator p = func->parameters().begin();
393 while (p != func->parameters().end()) {
394 if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
395 && func->name() != QLatin1String("operator++")
396 && func->name() != QLatin1String("operator--")) {
397 node->doc().location().warning(tr("Missing parameter name"));
399 else {
400 definedParams.insert((*p).name());
402 ++p;
405 QSet<QString> documentedParams = func->doc().parameterNames();
406 QSet<QString> allParams = definedParams + documentedParams;
407 if (allParams.count() > definedParams.count()
408 || allParams.count() > documentedParams.count()) {
409 QSet<QString>::ConstIterator a = allParams.begin();
410 while (a != allParams.end()) {
411 if (!definedParams.contains(*a)) {
412 QString details;
413 QString best = nearestName(*a, definedParams);
414 if (!best.isEmpty())
415 details = tr("Maybe you meant '%1'?").arg(best);
417 node->doc().location().warning(
418 tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
419 details);
421 else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
422 bool needWarning = (func->status() > Node::Obsolete);
423 if (func->overloadNumber() > 1) {
424 FunctionNode *primaryFunc =
425 func->parent()->findFunctionNode(func->name());
426 if (primaryFunc) {
427 foreach (const Parameter &param,
428 primaryFunc->parameters()) {
429 if (param.name() == *a) {
430 needWarning = false;
431 break;
436 if (needWarning && !func->isReimp())
437 node->doc().location().warning(
438 tr("Undocumented parameter '%1' in %2")
439 .arg(*a).arg(marker->plainFullName(node)));
441 ++a;
444 /* Something like this return value check should be implemented at some point. */
445 if (func->status() > Node::Obsolete && func->returnType() == "bool"
446 && func->reimplementedFrom() == 0 && !func->isOverload()) {
447 QString body = func->doc().body().toString();
448 if (!body.contains("return", Qt::CaseInsensitive))
449 node->doc().location().warning(tr("Undocumented return value"));
451 #if 0
452 // Now we put this at the top, before the other text.
453 if (func->reimplementedFrom() != 0)
454 generateReimplementedFrom(func, marker);
455 #endif
459 if (node->type() == Node::Fake) {
460 const FakeNode *fake = static_cast<const FakeNode *>(node);
461 if (fake->subType() == Node::File) {
462 Text text;
463 Quoter quoter;
464 Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
465 QString code = quoter.quoteTo(fake->location(), "", "");
466 text << Atom(Atom::Code, code);
467 generateText(text, fake, marker);
472 void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
474 QList<Text> alsoList = node->doc().alsoList();
475 supplementAlsoList(node, alsoList);
477 if (!alsoList.isEmpty()) {
478 Text text;
479 text << Atom::ParaLeft << "See also ";
481 for (int i = 0; i < alsoList.size(); ++i)
482 text << alsoList.at(i) << separator(i, alsoList.size());
484 text << Atom::ParaRight;
485 generateText(text, node, marker);
489 void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
491 QList<RelatedClass>::ConstIterator r;
492 int index;
494 if (!classe->baseClasses().isEmpty()) {
495 Text text;
496 text << Atom::ParaLeft << "Inherits ";
498 r = classe->baseClasses().begin();
499 index = 0;
500 while (r != classe->baseClasses().end()) {
501 text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
502 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
503 << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
504 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
506 if ((*r).access == Node::Protected) {
507 text << " (protected)";
509 else if ((*r).access == Node::Private) {
510 text << " (private)";
512 text << separator(index++, classe->baseClasses().count());
513 ++r;
515 text << Atom::ParaRight;
516 generateText(text, classe, marker);
520 #ifdef QDOC_QML
523 void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
525 // stub.
527 #endif
529 void Generator::generateInheritedBy(const ClassNode *classe,
530 CodeMarker *marker)
532 if (!classe->derivedClasses().isEmpty()) {
533 Text text;
534 text << Atom::ParaLeft << "Inherited by ";
536 appendSortedNames(text, classe, classe->derivedClasses(), marker);
537 text << Atom::ParaRight;
538 generateText(text, classe, marker);
543 This function is called when the documentation for an
544 example is being formatted. It outputs the list of source
545 files comprising the example, and the list of images used
546 by the example. The images are copied into a subtree of
547 \c{...doc/html/images/used-in-examples/...}
549 void Generator::generateFileList(const FakeNode* fake,
550 CodeMarker* marker,
551 Node::SubType subtype,
552 const QString& tag)
554 int count = 0;
555 Text text;
556 OpenedList openedList(OpenedList::Bullet);
558 text << Atom::ParaLeft << tag << Atom::ParaRight
559 << Atom(Atom::ListLeft, openedList.styleString());
561 foreach (const Node* child, fake->childNodes()) {
562 if (child->subType() == subtype) {
563 ++count;
564 QString file = child->name();
565 if (subtype == Node::Image) {
566 if (!file.isEmpty()) {
567 QDir dirInfo;
568 QString userFriendlyFilePath;
569 QString srcPath = Config::findFile(fake->location(),
570 QStringList(),
571 exampleDirs,
572 file,
573 exampleImgExts,
574 userFriendlyFilePath);
575 userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
577 QString imgOutDir = outDir + "/images/used-in-examples/" + userFriendlyFilePath;
578 if (!dirInfo.mkpath(imgOutDir))
579 fake->location().fatal(tr("Cannot create output directory '%1'")
580 .arg(imgOutDir));
582 QString imgOutName = Config::copyFile(fake->location(),
583 srcPath,
584 file,
585 imgOutDir);
590 openedList.next();
591 text << Atom(Atom::ListItemNumber, openedList.numberString())
592 << Atom(Atom::ListItemLeft, openedList.styleString())
593 << Atom::ParaLeft
594 << Atom(Atom::Link, file)
595 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
596 << file
597 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
598 << Atom::ParaRight
599 << Atom(Atom::ListItemRight, openedList.styleString());
602 text << Atom(Atom::ListRight, openedList.styleString());
603 if (count > 0)
604 generateText(text, fake, marker);
607 void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
609 if (fake->childNodes().isEmpty())
610 return;
611 generateFileList(fake, marker, Node::File, QString("Files:"));
612 generateFileList(fake, marker, Node::Image, QString("Images:"));
615 #if 0
616 QList<Generator *>::ConstIterator g = generators.begin();
617 while (g != generators.end()) {
618 if (outputFormats.contains((*g)->format())) {
619 (*g)->initializeGenerator(config);
620 QStringList extraImages =
621 config.getStringList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format());
622 QStringList::ConstIterator e = extraImages.begin();
623 while (e != extraImages.end()) {
624 QString userFriendlyFilePath;
625 QString filePath = Config::findFile(config.lastLocation(),
626 imageFiles,
627 imageDirs,
629 imgFileExts[(*g)->format()],
630 userFriendlyFilePath);
631 if (!filePath.isEmpty())
632 Config::copyFile(config.lastLocation(),
633 filePath,
634 userFriendlyFilePath,
635 (*g)->outputDir() +
636 "/images");
637 ++e;
640 ++g;
642 #endif
644 void Generator::generateModuleWarning(const ClassNode *classe,
645 CodeMarker *marker)
647 QString module = classe->moduleName();
648 if (!module.isEmpty()) {
649 Text text;
650 if (!editionModuleMap["DesktopLight"].contains(module)) {
651 text << Atom::ParaLeft
652 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
653 << "This class is not part of the Qt GUI Framework Edition."
654 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
655 << Atom::ParaRight;
657 else if (module == "Qt3Support") {
658 text << Atom::ParaLeft
659 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
660 << "Note to Qt GUI Framework Edition users:"
661 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
662 << " This class is only available in the "
663 << Atom(Atom::AutoLink, "Qt Full Framework Edition")
664 << "." << Atom::ParaRight;
667 generateText(text, classe, marker);
671 QString Generator::indent(int level, const QString& markedCode)
673 if (level == 0)
674 return markedCode;
676 QString t;
677 int column = 0;
679 int i = 0;
680 while (i < (int) markedCode.length()) {
681 if (markedCode.at(i) == QLatin1Char('<')) {
682 while (i < (int) markedCode.length()) {
683 t += markedCode.at(i++);
684 if (markedCode.at(i - 1) == QLatin1Char('>'))
685 break;
688 else {
689 if (markedCode.at(i) == QLatin1Char('\n')) {
690 column = 0;
692 else {
693 if (column == 0) {
694 for (int j = 0; j < level; j++)
695 t += QLatin1Char(' ');
697 column++;
699 t += markedCode.at(i++);
702 return t;
705 QString Generator::plainCode(const QString& markedCode)
707 QString t = markedCode;
708 t.replace(tag, QString());
709 t.replace(quot, QLatin1String("\""));
710 t.replace(gt, QLatin1String(">"));
711 t.replace(lt, QLatin1String("<"));
712 t.replace(amp, QLatin1String("&"));
713 return t;
716 QString Generator::typeString(const Node *node)
718 switch (node->type()) {
719 case Node::Namespace:
720 return "namespace";
721 case Node::Class:
722 return "class";
723 case Node::Fake:
724 default:
725 return "documentation";
726 case Node::Enum:
727 return "enum";
728 case Node::Typedef:
729 return "typedef";
730 case Node::Function:
731 return "function";
732 case Node::Property:
733 return "property";
737 QString Generator::imageFileName(const Node *relative, const QString& fileBase)
739 QString userFriendlyFilePath;
740 QString filePath = Config::findFile(
741 relative->doc().location(), imageFiles, imageDirs, fileBase,
742 imgFileExts[format()], userFriendlyFilePath);
744 if (filePath.isEmpty())
745 return QString();
747 return QLatin1String("images/")
748 + Config::copyFile(relative->doc().location(),
749 filePath, userFriendlyFilePath,
750 outputDir() + QLatin1String("/images"));
753 void Generator::setImageFileExtensions(const QStringList& extensions)
755 imgFileExts[format()] = extensions;
758 void Generator::unknownAtom(const Atom *atom)
760 Location::internalError(tr("unknown atom type '%1' in %2 generator")
761 .arg(atom->typeString()).arg(format()));
764 bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
766 return atom->next() != 0 && atom->next()->type() == expectedAtomType;
769 void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
771 if (node->type() == Node::Function) {
772 const FunctionNode *func = static_cast<const FunctionNode *>(node);
773 if (func->overloadNumber() == 1) {
774 QString alternateName;
775 const FunctionNode *alternateFunc = 0;
777 if (func->name().startsWith("set") && func->name().size() >= 4) {
778 alternateName = func->name()[3].toLower();
779 alternateName += func->name().mid(4);
780 alternateFunc = func->parent()->findFunctionNode(alternateName);
782 if (!alternateFunc) {
783 alternateName = "is" + func->name().mid(3);
784 alternateFunc = func->parent()->findFunctionNode(alternateName);
785 if (!alternateFunc) {
786 alternateName = "has" + func->name().mid(3);
787 alternateFunc = func->parent()->findFunctionNode(alternateName);
791 else if (!func->name().isEmpty()) {
792 alternateName = "set";
793 alternateName += func->name()[0].toUpper();
794 alternateName += func->name().mid(1);
795 alternateFunc = func->parent()->findFunctionNode(alternateName);
798 if (alternateFunc && alternateFunc->access() != Node::Private) {
799 int i;
800 for (i = 0; i < alsoList.size(); ++i) {
801 if (alsoList.at(i).toString().contains(alternateName))
802 break;
805 if (i == alsoList.size()) {
806 alternateName += "()";
808 Text also;
809 also << Atom(Atom::Link, alternateName)
810 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
811 << alternateName
812 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
813 alsoList.prepend(also);
820 QMap<QString, QString>& Generator::formattingLeftMap()
822 return fmtLeftMaps[format()];
825 QMap<QString, QString>& Generator::formattingRightMap()
827 return fmtRightMaps[format()];
830 QString Generator::trimmedTrailing(const QString &string)
832 QString trimmed = string;
833 while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
834 trimmed.truncate(trimmed.length() - 1);
835 return trimmed;
838 void Generator::generateStatus(const Node *node, CodeMarker *marker)
840 Text text;
842 switch (node->status()) {
843 case Node::Commendable:
844 case Node::Main:
845 break;
846 case Node::Preliminary:
847 text << Atom::ParaLeft
848 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
849 << "This "
850 << typeString(node)
851 << " is under development and is subject to change."
852 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
853 << Atom::ParaRight;
854 break;
855 case Node::Deprecated:
856 text << Atom::ParaLeft;
857 if (node->isInnerNode())
858 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
859 text << "This " << typeString(node) << " is deprecated.";
860 if (node->isInnerNode())
861 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
862 text << Atom::ParaRight;
863 break;
864 case Node::Obsolete:
865 text << Atom::ParaLeft;
866 if (node->isInnerNode())
867 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
868 text << "This " << typeString(node) << " is obsolete.";
869 if (node->isInnerNode())
870 text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
871 text << " It is provided to keep old source code working. "
872 << "We strongly advise against "
873 << "using it in new code." << Atom::ParaRight;
874 break;
875 case Node::Compat:
876 // reimplemented in HtmlGenerator subclass
877 if (node->isInnerNode()) {
878 text << Atom::ParaLeft
879 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
880 << "This "
881 << typeString(node)
882 << " is part of the Qt 3 compatibility layer."
883 << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
884 << " It is provided to keep old source code working. "
885 << "We strongly advise against "
886 << "using it in new code. See "
887 << Atom(Atom::AutoLink, "Porting to Qt 4")
888 << " for more information."
889 << Atom::ParaRight;
891 break;
892 case Node::Internal:
893 default:
894 break;
896 generateText(text, node, marker);
899 void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
901 Text text;
902 Text theStockLink;
903 Node::ThreadSafeness threadSafeness = node->threadSafeness();
905 Text rlink;
906 rlink << Atom(Atom::Link,"reentrant")
907 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
908 << "reentrant"
909 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
911 Text tlink;
912 tlink << Atom(Atom::Link,"thread-safe")
913 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
914 << "thread-safe"
915 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
917 switch (threadSafeness) {
918 case Node::UnspecifiedSafeness:
919 break;
920 case Node::NonReentrant:
921 text << Atom::ParaLeft
922 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
923 << "Warning:"
924 << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
925 << " This "
926 << typeString(node)
927 << " is not "
928 << rlink
929 << "."
930 << Atom::ParaRight;
931 break;
932 case Node::Reentrant:
933 case Node::ThreadSafe:
934 text << Atom::ParaLeft
935 << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
936 << "Note:"
937 << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
938 << " ";
940 if (node->isInnerNode()) {
941 const InnerNode* innerNode = static_cast<const InnerNode*>(node);
942 text << "All functions in this "
943 << typeString(node)
944 << " are ";
945 if (threadSafeness == Node::ThreadSafe)
946 text << tlink;
947 else
948 text << rlink;
950 bool exceptions = false;
951 NodeList reentrant;
952 NodeList threadsafe;
953 NodeList nonreentrant;
954 NodeList::ConstIterator c = innerNode->childNodes().begin();
955 while (c != innerNode->childNodes().end()) {
956 switch ((*c)->threadSafeness()) {
957 case Node::Reentrant:
958 reentrant.append(*c);
959 if (threadSafeness == Node::ThreadSafe)
960 exceptions = true;
961 break;
962 case Node::ThreadSafe:
963 threadsafe.append(*c);
964 if (threadSafeness == Node::Reentrant)
965 exceptions = true;
966 break;
967 case Node::NonReentrant:
968 nonreentrant.append(*c);
969 exceptions = true;
970 break;
971 default:
972 break;
974 ++c;
976 if (!exceptions)
977 text << ".";
978 else if (threadSafeness == Node::Reentrant) {
979 if (nonreentrant.isEmpty()) {
980 if (!threadsafe.isEmpty()) {
981 text << ", but ";
982 appendFullNames(text,threadsafe,innerNode,marker);
983 singularPlural(text,threadsafe);
984 text << " also " << tlink << ".";
986 else
987 text << ".";
989 else {
990 text << ", except for ";
991 appendFullNames(text,nonreentrant,innerNode,marker);
992 text << ", which";
993 singularPlural(text,nonreentrant);
994 text << " nonreentrant.";
995 if (!threadsafe.isEmpty()) {
996 text << " ";
997 appendFullNames(text,threadsafe,innerNode,marker);
998 singularPlural(text,threadsafe);
999 text << " " << tlink << ".";
1003 else { // thread-safe
1004 if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) {
1005 text << ", except for ";
1006 if (!reentrant.isEmpty()) {
1007 appendFullNames(text,reentrant,innerNode,marker);
1008 text << ", which";
1009 singularPlural(text,reentrant);
1010 text << " only " << rlink;
1011 if (!nonreentrant.isEmpty())
1012 text << ", and ";
1014 if (!nonreentrant.isEmpty()) {
1015 appendFullNames(text,nonreentrant,innerNode,marker);
1016 text << ", which";
1017 singularPlural(text,nonreentrant);
1018 text << " nonreentrant.";
1020 text << ".";
1024 else {
1025 text << "This " << typeString(node) << " is ";
1026 if (threadSafeness == Node::ThreadSafe)
1027 text << tlink;
1028 else
1029 text << rlink;
1030 text << ".";
1032 text << Atom::ParaRight;
1034 generateText(text,node,marker);
1037 void Generator::generateSince(const Node *node, CodeMarker *marker)
1039 if (!node->since().isEmpty()) {
1040 Text text;
1041 text << Atom::ParaLeft
1042 << "This "
1043 << typeString(node)
1044 << " was introduced in ";
1045 if (project.isEmpty())
1046 text << "version";
1047 else
1048 text << project;
1049 text << " " << node->since() << "." << Atom::ParaRight;
1050 generateText(text, node, marker);
1055 No longer in use.
1057 void Generator::generateOverload(const Node *node, CodeMarker *marker)
1059 Text text;
1060 text << Atom::ParaLeft
1061 << "This function overloads ";
1062 QString t = node->name() + "()";
1063 text << Atom::AutoLink << t
1064 << Atom::ParaRight;
1065 generateText(text, node, marker);
1068 void Generator::generateReimplementedFrom(const FunctionNode *func,
1069 CodeMarker *marker)
1071 if (func->reimplementedFrom() != 0) {
1072 const FunctionNode *from = func->reimplementedFrom();
1073 if (from->access() != Node::Private &&
1074 from->parent()->access() != Node::Private) {
1075 Text text;
1076 text << Atom::ParaLeft << "Reimplemented from ";
1077 QString fullName = from->parent()->name() + "::" + from->name() + "()";
1078 appendFullName(text, from->parent(), fullName, from);
1079 text << "." << Atom::ParaRight;
1080 generateText(text, func, marker);
1085 const Atom *Generator::generateAtomList(const Atom *atom,
1086 const Node *relative,
1087 CodeMarker *marker,
1088 bool generate,
1089 int &numAtoms)
1091 while (atom) {
1092 if (atom->type() == Atom::FormatIf) {
1093 int numAtoms0 = numAtoms;
1094 bool rightFormat = canHandleFormat(atom->string());
1095 atom = generateAtomList(atom->next(),
1096 relative,
1097 marker,
1098 generate && rightFormat,
1099 numAtoms);
1100 if (!atom)
1101 return 0;
1103 if (atom->type() == Atom::FormatElse) {
1104 ++numAtoms;
1105 atom = generateAtomList(atom->next(),
1106 relative,
1107 marker,
1108 generate && !rightFormat,
1109 numAtoms);
1110 if (!atom)
1111 return 0;
1114 if (atom->type() == Atom::FormatEndif) {
1115 if (generate && numAtoms0 == numAtoms) {
1116 relative->location().warning(tr("Output format %1 not handled")
1117 .arg(format()));
1118 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
1119 generateAtomList(&unhandledFormatAtom,
1120 relative,
1121 marker,
1122 generate,
1123 numAtoms);
1125 atom = atom->next();
1128 else if (atom->type() == Atom::FormatElse ||
1129 atom->type() == Atom::FormatEndif) {
1130 return atom;
1132 else {
1133 int n = 1;
1134 if (generate) {
1135 n += generateAtom(atom, relative, marker);
1136 numAtoms += n;
1138 while (n-- > 0)
1139 atom = atom->next();
1142 return 0;
1145 void Generator::appendFullName(Text& text,
1146 const Node *apparentNode,
1147 const Node *relative,
1148 CodeMarker *marker,
1149 const Node *actualNode)
1151 if (actualNode == 0)
1152 actualNode = apparentNode;
1153 text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
1154 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1155 << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
1156 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1159 void Generator::appendFullName(Text& text,
1160 const Node *apparentNode,
1161 const QString& fullName,
1162 const Node *actualNode)
1164 if (actualNode == 0)
1165 actualNode = apparentNode;
1166 text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
1167 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1168 << Atom(Atom::String, fullName)
1169 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1172 void Generator::appendFullNames(Text& text,
1173 const NodeList& nodes,
1174 const Node* relative,
1175 CodeMarker* marker)
1177 NodeList::ConstIterator n = nodes.begin();
1178 int index = 0;
1179 while (n != nodes.end()) {
1180 appendFullName(text,*n,relative,marker);
1181 text << comma(index++,nodes.count());
1182 ++n;
1186 void Generator::appendSortedNames(Text& text,
1187 const ClassNode *classe,
1188 const QList<RelatedClass> &classes,
1189 CodeMarker *marker)
1191 QList<RelatedClass>::ConstIterator r;
1192 QMap<QString,Text> classMap;
1193 int index = 0;
1195 r = classes.begin();
1196 while (r != classes.end()) {
1197 if ((*r).node->access() == Node::Public &&
1198 (*r).node->status() != Node::Internal
1199 && !(*r).node->doc().isEmpty()) {
1200 Text className;
1201 appendFullName(className, (*r).node, classe, marker);
1202 classMap[className.toString().toLower()] = className;
1204 ++r;
1207 QStringList classNames = classMap.keys();
1208 classNames.sort();
1210 foreach (const QString &className, classNames) {
1211 text << classMap[className];
1212 text << separator(index++, classNames.count());
1216 int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
1218 int skipAhead = 0;
1219 atom = atom->next();
1220 while (atom != 0 && atom->type() != type) {
1221 skipAhead++;
1222 atom = atom->next();
1224 return skipAhead;
1227 QString Generator::fullName(const Node *node,
1228 const Node *relative,
1229 CodeMarker *marker) const
1231 if (node->type() == Node::Fake)
1232 return static_cast<const FakeNode *>(node)->title();
1233 else if (node->type() == Node::Class &&
1234 !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
1235 return (static_cast<const ClassNode *>(node))->serviceName();
1236 else
1237 return marker->plainFullName(node, relative);
1240 QT_END_NAMESPACE