1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
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
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.
40 ****************************************************************************/
48 #include "codemarker.h"
51 #include "editdistance.h"
52 #include "generator.h"
54 #include "openedlist.h"
56 #include "separator.h"
57 #include "tokenizer.h"
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)
81 Generator::Generator()
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"));
113 if (dirInfo
.exists(outDir
)) {
114 if (!Config::removeDirContents(outDir
))
115 config
.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir
));
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
+
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(),
159 imgFileExts
[(*g
)->format()],
160 userFriendlyFilePath
);
161 if (!filePath
.isEmpty())
162 Config::copyFile(config
.lastLocation(),
164 userFriendlyFilePath
,
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
+
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 "
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' "
199 .arg(*n
).arg(numOccs
));
202 int paramPos
= def
.indexOf("\1");
203 fmtLeftMaps
[*f
].insert(*n
, def
.left(paramPos
));
204 fmtRightMaps
[*f
].insert(*n
, def
.mid(paramPos
+ 1));
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();
225 fmtRightMaps
.clear();
232 Generator
*Generator::generatorForFormat(const QString
& format
)
234 QList
<Generator
*>::ConstIterator g
= generators
.begin();
235 while (g
!= generators
.end()) {
236 if ((*g
)->format() == format
)
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 */)
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
,
274 if (text
.firstAtom() != 0) {
276 startText(relative
, marker
);
277 generateAtomList(text
.firstAtom(),
282 endText(relative
, marker
);
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
,
296 const QString
& /* qmlName */ )
298 const Atom
* atom
= text
.firstAtom();
302 startText(relative
, marker
);
304 if (atom
->type() != Atom::QmlText
)
308 while (atom
&& (atom
->type() != Atom::EndQmlText
)) {
309 int n
= 1 + generateAtom(atom
, relative
, marker
);
315 endText(relative
, marker
);
320 void Generator::generateBody(const Node
*node
, CodeMarker
*marker
)
324 if (node
->type() == Node::Function
) {
326 const FunctionNode
*func
= (const FunctionNode
*) node
;
327 if (func
->isOverload() && func
->metaness() != FunctionNode::Ctor
)
328 generateOverload(node
, marker
);
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
))
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
)));
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
))
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());
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
)) {
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
)),
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
)));
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"));
400 definedParams
.insert((*p
).name());
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
)) {
413 QString best
= nearestName(*a
, definedParams
);
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
)),
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());
427 foreach (const Parameter
¶m
,
428 primaryFunc
->parameters()) {
429 if (param
.name() == *a
) {
436 if (needWarning
&& !func
->isReimp())
437 node
->doc().location().warning(
438 tr("Undocumented parameter '%1' in %2")
439 .arg(*a
).arg(marker
->plainFullName(node
)));
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"));
452 // Now we put this at the top, before the other text.
453 if (func
->reimplementedFrom() != 0)
454 generateReimplementedFrom(func
, marker
);
459 if (node
->type() == Node::Fake
) {
460 const FakeNode
*fake
= static_cast<const FakeNode
*>(node
);
461 if (fake
->subType() == Node::File
) {
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()) {
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
;
494 if (!classe
->baseClasses().isEmpty()) {
496 text
<< Atom::ParaLeft
<< "Inherits ";
498 r
= classe
->baseClasses().begin();
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());
515 text
<< Atom::ParaRight
;
516 generateText(text
, classe
, marker
);
523 void Generator::generateQmlInherits(const QmlClassNode
* , CodeMarker
* )
529 void Generator::generateInheritedBy(const ClassNode
*classe
,
532 if (!classe
->derivedClasses().isEmpty()) {
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
,
551 Node::SubType subtype
,
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
) {
564 QString file
= child
->name();
565 if (subtype
== Node::Image
) {
566 if (!file
.isEmpty()) {
568 QString userFriendlyFilePath
;
569 QString srcPath
= Config::findFile(fake
->location(),
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'")
582 QString imgOutName
= Config::copyFile(fake
->location(),
591 text
<< Atom(Atom::ListItemNumber
, openedList
.numberString())
592 << Atom(Atom::ListItemLeft
, openedList
.styleString())
594 << Atom(Atom::Link
, file
)
595 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
597 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_LINK
)
599 << Atom(Atom::ListItemRight
, openedList
.styleString());
602 text
<< Atom(Atom::ListRight
, openedList
.styleString());
604 generateText(text
, fake
, marker
);
607 void Generator::generateExampleFiles(const FakeNode
*fake
, CodeMarker
*marker
)
609 if (fake
->childNodes().isEmpty())
611 generateFileList(fake
, marker
, Node::File
, QString("Files:"));
612 generateFileList(fake
, marker
, Node::Image
, QString("Images:"));
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(),
629 imgFileExts
[(*g
)->format()],
630 userFriendlyFilePath
);
631 if (!filePath
.isEmpty())
632 Config::copyFile(config
.lastLocation(),
634 userFriendlyFilePath
,
644 void Generator::generateModuleWarning(const ClassNode
*classe
,
647 QString module
= classe
->moduleName();
648 if (!module
.isEmpty()) {
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
)
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
)
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('>'))
689 if (markedCode
.at(i
) == QLatin1Char('\n')) {
694 for (int j
= 0; j
< level
; j
++)
695 t
+= QLatin1Char(' ');
699 t
+= markedCode
.at(i
++);
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("&"));
716 QString
Generator::typeString(const Node
*node
)
718 switch (node
->type()) {
719 case Node::Namespace
:
725 return "documentation";
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())
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
) {
800 for (i
= 0; i
< alsoList
.size(); ++i
) {
801 if (alsoList
.at(i
).toString().contains(alternateName
))
805 if (i
== alsoList
.size()) {
806 alternateName
+= "()";
809 also
<< Atom(Atom::Link
, alternateName
)
810 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
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);
838 void Generator::generateStatus(const Node
*node
, CodeMarker
*marker
)
842 switch (node
->status()) {
843 case Node::Commendable
:
846 case Node::Preliminary
:
847 text
<< Atom::ParaLeft
848 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_BOLD
)
851 << " is under development and is subject to change."
852 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_BOLD
)
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
;
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
;
876 // reimplemented in HtmlGenerator subclass
877 if (node
->isInnerNode()) {
878 text
<< Atom::ParaLeft
879 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_BOLD
)
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."
896 generateText(text
, node
, marker
);
899 void Generator::generateThreadSafeness(const Node
*node
, CodeMarker
*marker
)
903 Node::ThreadSafeness threadSafeness
= node
->threadSafeness();
906 rlink
<< Atom(Atom::Link
,"reentrant")
907 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
909 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
912 tlink
<< Atom(Atom::Link
,"thread-safe")
913 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
915 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
917 switch (threadSafeness
) {
918 case Node::UnspecifiedSafeness
:
920 case Node::NonReentrant
:
921 text
<< Atom::ParaLeft
922 << Atom(Atom::FormattingLeft
,ATOM_FORMATTING_BOLD
)
924 << Atom(Atom::FormattingRight
,ATOM_FORMATTING_BOLD
)
932 case Node::Reentrant
:
933 case Node::ThreadSafe
:
934 text
<< Atom::ParaLeft
935 << Atom(Atom::FormattingLeft
,ATOM_FORMATTING_BOLD
)
937 << Atom(Atom::FormattingRight
,ATOM_FORMATTING_BOLD
)
940 if (node
->isInnerNode()) {
941 const InnerNode
* innerNode
= static_cast<const InnerNode
*>(node
);
942 text
<< "All functions in this "
945 if (threadSafeness
== Node::ThreadSafe
)
950 bool exceptions
= false;
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
)
962 case Node::ThreadSafe
:
963 threadsafe
.append(*c
);
964 if (threadSafeness
== Node::Reentrant
)
967 case Node::NonReentrant
:
968 nonreentrant
.append(*c
);
978 else if (threadSafeness
== Node::Reentrant
) {
979 if (nonreentrant
.isEmpty()) {
980 if (!threadsafe
.isEmpty()) {
982 appendFullNames(text
,threadsafe
,innerNode
,marker
);
983 singularPlural(text
,threadsafe
);
984 text
<< " also " << tlink
<< ".";
990 text
<< ", except for ";
991 appendFullNames(text
,nonreentrant
,innerNode
,marker
);
993 singularPlural(text
,nonreentrant
);
994 text
<< " nonreentrant.";
995 if (!threadsafe
.isEmpty()) {
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
);
1009 singularPlural(text
,reentrant
);
1010 text
<< " only " << rlink
;
1011 if (!nonreentrant
.isEmpty())
1014 if (!nonreentrant
.isEmpty()) {
1015 appendFullNames(text
,nonreentrant
,innerNode
,marker
);
1017 singularPlural(text
,nonreentrant
);
1018 text
<< " nonreentrant.";
1025 text
<< "This " << typeString(node
) << " is ";
1026 if (threadSafeness
== Node::ThreadSafe
)
1032 text
<< Atom::ParaRight
;
1034 generateText(text
,node
,marker
);
1037 void Generator::generateSince(const Node
*node
, CodeMarker
*marker
)
1039 if (!node
->since().isEmpty()) {
1041 text
<< Atom::ParaLeft
1044 << " was introduced in ";
1045 if (project
.isEmpty())
1049 text
<< " " << node
->since() << "." << Atom::ParaRight
;
1050 generateText(text
, node
, marker
);
1057 void Generator::generateOverload(const Node
*node
, CodeMarker
*marker
)
1060 text
<< Atom::ParaLeft
1061 << "This function overloads ";
1062 QString t
= node
->name() + "()";
1063 text
<< Atom::AutoLink
<< t
1065 generateText(text
, node
, marker
);
1068 void Generator::generateReimplementedFrom(const FunctionNode
*func
,
1071 if (func
->reimplementedFrom() != 0) {
1072 const FunctionNode
*from
= func
->reimplementedFrom();
1073 if (from
->access() != Node::Private
&&
1074 from
->parent()->access() != Node::Private
) {
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
,
1092 if (atom
->type() == Atom::FormatIf
) {
1093 int numAtoms0
= numAtoms
;
1094 bool rightFormat
= canHandleFormat(atom
->string());
1095 atom
= generateAtomList(atom
->next(),
1098 generate
&& rightFormat
,
1103 if (atom
->type() == Atom::FormatElse
) {
1105 atom
= generateAtomList(atom
->next(),
1108 generate
&& !rightFormat
,
1114 if (atom
->type() == Atom::FormatEndif
) {
1115 if (generate
&& numAtoms0
== numAtoms
) {
1116 relative
->location().warning(tr("Output format %1 not handled")
1118 Atom
unhandledFormatAtom(Atom::UnhandledFormat
, format());
1119 generateAtomList(&unhandledFormatAtom
,
1125 atom
= atom
->next();
1128 else if (atom
->type() == Atom::FormatElse
||
1129 atom
->type() == Atom::FormatEndif
) {
1135 n
+= generateAtom(atom
, relative
, marker
);
1139 atom
= atom
->next();
1145 void Generator::appendFullName(Text
& text
,
1146 const Node
*apparentNode
,
1147 const Node
*relative
,
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
,
1177 NodeList::ConstIterator n
= nodes
.begin();
1179 while (n
!= nodes
.end()) {
1180 appendFullName(text
,*n
,relative
,marker
);
1181 text
<< comma(index
++,nodes
.count());
1186 void Generator::appendSortedNames(Text
& text
,
1187 const ClassNode
*classe
,
1188 const QList
<RelatedClass
> &classes
,
1191 QList
<RelatedClass
>::ConstIterator r
;
1192 QMap
<QString
,Text
> classMap
;
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()) {
1201 appendFullName(className
, (*r
).node
, classe
, marker
);
1202 classMap
[className
.toString().toLower()] = className
;
1207 QStringList classNames
= classMap
.keys();
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
1219 atom
= atom
->next();
1220 while (atom
!= 0 && atom
->type() != type
) {
1222 atom
= atom
->next();
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();
1237 return marker
->plainFullName(node
, relative
);