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

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 43.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
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 have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 generator.cpp
44*/
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"
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;
68QStringList Generator::exampleDirs;
69QStringList Generator::exampleImgExts;
70QString Generator::outDir;
71QString Generator::project;
72
73static void singularPlural(Text& text, const NodeList& nodes)
74{
75 if (nodes.count() == 1)
76 text << " is";
77 else
78 text << " are";
79}
80
81Generator::Generator()
82 : amp("&amp;"),
83 lt("&lt;"),
84 gt("&gt;"),
85 quot("&quot;"),
86 tag("</?@[^>]*>")
87{
88 generators.prepend(this);
89}
90
91Generator::~Generator()
92{
93 generators.removeAll(this);
94}
95
96void Generator::initializeGenerator(const Config & /* config */)
97{
98}
99
100void Generator::terminateGenerator()
101{
102}
103
104void Generator::initialize(const Config &config)
105{
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"));
111
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));
116 }
117 else {
118 if (!dirInfo.mkpath(outDir))
119 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
120 }
121
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"));
128 }
129
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);
135
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;
144 }
145
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,
158 *e,
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;
168 }
169 }
170 ++g;
171 }
172
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;
178
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");
187
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));
193 }
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));
200 }
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));
205 }
206 }
207 ++f;
208 }
209 ++n;
210 }
211
212 project = config.getString(CONFIG_PROJECT);
213}
214
215void Generator::terminate()
216{
217 QList<Generator *>::ConstIterator g = generators.begin();
218 while (g != generators.end()) {
219 if (outputFormats.contains((*g)->format()))
220 (*g)->terminateGenerator();
221 ++g;
222 }
223
224 fmtLeftMaps.clear();
225 fmtRightMaps.clear();
226 imgFileExts.clear();
227 imageFiles.clear();
228 imageDirs.clear();
229 outDir = "";
230}
231
232Generator *Generator::generatorForFormat(const QString& format)
233{
234 QList<Generator *>::ConstIterator g = generators.begin();
235 while (g != generators.end()) {
236 if ((*g)->format() == format)
237 return *g;
238 ++g;
239 }
240 return 0;
241}
242
243void Generator::startText(const Node * /* relative */,
244 CodeMarker * /* marker */)
245{
246}
247
248void Generator::endText(const Node * /* relative */,
249 CodeMarker * /* marker */)
250{
251}
252
253int Generator::generateAtom(const Atom * /* atom */,
254 const Node * /* relative */,
255 CodeMarker * /* marker */)
256{
257 return 0;
258}
259
260void Generator::generateClassLikeNode(const InnerNode * /* classe */,
261 CodeMarker * /* marker */)
262{
263}
264
265void Generator::generateFakeNode(const FakeNode * /* fake */,
266 CodeMarker * /* marker */)
267{
268}
269
270bool Generator::generateText(const Text& text,
271 const Node *relative,
272 CodeMarker *marker)
273{
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;
284 }
285 return false;
286}
287
288#ifdef QDOC_QML
289/*!
290 Extract sections of markup text surrounded by \e qmltext
291 and \e endqmltext and output them.
292 */
293bool Generator::generateQmlText(const Text& text,
294 const Node *relative,
295 CodeMarker *marker,
296 const QString& /* qmlName */ )
297{
298 const Atom* atom = text.firstAtom();
299 if (atom == 0)
300 return false;
301
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();
312 }
313 }
314 }
315 endText(relative, marker);
316 return true;
317}
318#endif
319
320void Generator::generateBody(const Node *node, CodeMarker *marker)
321{
322 bool quiet = false;
323
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
330 }
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;
337 }
338
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)));
343 }
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);
349 }
350
351 if (!generateText(node->doc().body(), node, marker))
352 if (node->isReimp())
353 return;
354
355 if (node->type() == Node::Enum) {
356 const EnumNode *enume = (const EnumNode *) node;
357
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;
363 }
364
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);
376
377 node->doc().location().warning(
378 tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
379 details);
380 }
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)));
384 }
385 ++a;
386 }
387 }
388 }
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"));
398 }
399 else {
400 definedParams.insert((*p).name());
401 }
402 ++p;
403 }
404
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);
416
417 node->doc().location().warning(
418 tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
419 details);
420 }
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;
432 }
433 }
434 }
435 }
436 if (needWarning && !func->isReimp())
437 node->doc().location().warning(
438 tr("Undocumented parameter '%1' in %2")
439 .arg(*a).arg(marker->plainFullName(node)));
440 }
441 ++a;
442 }
443 }
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"));
450 }
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
456 }
457 }
458
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);
468 }
469 }
470}
471
472void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
473{
474 QList<Text> alsoList = node->doc().alsoList();
475 supplementAlsoList(node, alsoList);
476
477 if (!alsoList.isEmpty()) {
478 Text text;
479 text << Atom::ParaLeft << "See also ";
480
481 for (int i = 0; i < alsoList.size(); ++i)
482 text << alsoList.at(i) << separator(i, alsoList.size());
483
484 text << Atom::ParaRight;
485 generateText(text, node, marker);
486 }
487}
488
489void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
490{
491 QList<RelatedClass>::ConstIterator r;
492 int index;
493
494 if (!classe->baseClasses().isEmpty()) {
495 Text text;
496 text << Atom::ParaLeft << "Inherits ";
497
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);
505
506 if ((*r).access == Node::Protected) {
507 text << " (protected)";
508 }
509 else if ((*r).access == Node::Private) {
510 text << " (private)";
511 }
512 text << separator(index++, classe->baseClasses().count());
513 ++r;
514 }
515 text << Atom::ParaRight;
516 generateText(text, classe, marker);
517 }
518}
519
520#ifdef QDOC_QML
521/*!
522 */
523void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
524{
525 // stub.
526}
527#endif
528
529void Generator::generateInheritedBy(const ClassNode *classe,
530 CodeMarker *marker)
531{
532 if (!classe->derivedClasses().isEmpty()) {
533 Text text;
534 text << Atom::ParaLeft << "Inherited by ";
535
536 appendSortedNames(text, classe, classe->derivedClasses(), marker);
537 text << Atom::ParaRight;
538 generateText(text, classe, marker);
539 }
540}
541
542/*!
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/...}
548 */
549void Generator::generateFileList(const FakeNode* fake,
550 CodeMarker* marker,
551 Node::SubType subtype,
552 const QString& tag)
553{
554 int count = 0;
555 Text text;
556 OpenedList openedList(OpenedList::Bullet);
557
558 text << Atom::ParaLeft << tag << Atom::ParaRight
559 << Atom(Atom::ListLeft, openedList.styleString());
560
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('/'));
576
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));
581
582 QString imgOutName = Config::copyFile(fake->location(),
583 srcPath,
584 file,
585 imgOutDir);
586 }
587
588 }
589
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());
600 }
601 }
602 text << Atom(Atom::ListRight, openedList.styleString());
603 if (count > 0)
604 generateText(text, fake, marker);
605}
606
607void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
608{
609 if (fake->childNodes().isEmpty())
610 return;
611 generateFileList(fake, marker, Node::File, QString("Files:"));
612 generateFileList(fake, marker, Node::Image, QString("Images:"));
613}
614
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,
628 *e,
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;
638 }
639 }
640 ++g;
641 }
642#endif
643
644void Generator::generateModuleWarning(const ClassNode *classe,
645 CodeMarker *marker)
646{
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;
656 }
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;
665 }
666
667 generateText(text, classe, marker);
668 }
669}
670
671QString Generator::indent(int level, const QString& markedCode)
672{
673 if (level == 0)
674 return markedCode;
675
676 QString t;
677 int column = 0;
678
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;
686 }
687 }
688 else {
689 if (markedCode.at(i) == QLatin1Char('\n')) {
690 column = 0;
691 }
692 else {
693 if (column == 0) {
694 for (int j = 0; j < level; j++)
695 t += QLatin1Char(' ');
696 }
697 column++;
698 }
699 t += markedCode.at(i++);
700 }
701 }
702 return t;
703}
704
705QString Generator::plainCode(const QString& markedCode)
706{
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;
714}
715
716QString Generator::typeString(const Node *node)
717{
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";
734 }
735}
736
737QString Generator::imageFileName(const Node *relative, const QString& fileBase)
738{
739 QString userFriendlyFilePath;
740 QString filePath = Config::findFile(
741 relative->doc().location(), imageFiles, imageDirs, fileBase,
742 imgFileExts[format()], userFriendlyFilePath);
743
744 if (filePath.isEmpty())
745 return QString();
746
747 return QLatin1String("images/")
748 + Config::copyFile(relative->doc().location(),
749 filePath, userFriendlyFilePath,
750 outputDir() + QLatin1String("/images"));
751}
752
753void Generator::setImageFileExtensions(const QStringList& extensions)
754{
755 imgFileExts[format()] = extensions;
756}
757
758void Generator::unknownAtom(const Atom *atom)
759{
760 Location::internalError(tr("unknown atom type '%1' in %2 generator")
761 .arg(atom->typeString()).arg(format()));
762}
763
764bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
765{
766 return atom->next() != 0 && atom->next()->type() == expectedAtomType;
767}
768
769void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
770{
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;
776
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);
781
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);
788 }
789 }
790 }
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);
796 }
797
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;
803 }
804
805 if (i == alsoList.size()) {
806 alternateName += "()";
807
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);
814 }
815 }
816 }
817 }
818}
819
820QMap<QString, QString>& Generator::formattingLeftMap()
821{
822 return fmtLeftMaps[format()];
823}
824
825QMap<QString, QString>& Generator::formattingRightMap()
826{
827 return fmtRightMaps[format()];
828}
829
830QString Generator::trimmedTrailing(const QString &string)
831{
832 QString trimmed = string;
833 while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
834 trimmed.truncate(trimmed.length() - 1);
835 return trimmed;
836}
837
838void Generator::generateStatus(const Node *node, CodeMarker *marker)
839{
840 Text text;
841
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;
890 }
891 break;
892 case Node::Internal:
893 default:
894 break;
895 }
896 generateText(text, node, marker);
897}
898
899void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
900{
901 Text text;
902 Text theStockLink;
903 Node::ThreadSafeness threadSafeness = node->threadSafeness();
904
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);
910
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);
916
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 << " ";
939
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;
949
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;
973 }
974 ++c;
975 }
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 << ".";
985 }
986 else
987 text << ".";
988 }
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 << ".";
1000 }
1001 }
1002 }
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 ";
1013 }
1014 if (!nonreentrant.isEmpty()) {
1015 appendFullNames(text,nonreentrant,innerNode,marker);
1016 text << ", which";
1017 singularPlural(text,nonreentrant);
1018 text << " nonreentrant.";
1019 }
1020 text << ".";
1021 }
1022 }
1023 }
1024 else {
1025 text << "This " << typeString(node) << " is ";
1026 if (threadSafeness == Node::ThreadSafe)
1027 text << tlink;
1028 else
1029 text << rlink;
1030 text << ".";
1031 }
1032 text << Atom::ParaRight;
1033 }
1034 generateText(text,node,marker);
1035}
1036
1037void Generator::generateSince(const Node *node, CodeMarker *marker)
1038{
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);
1051 }
1052}
1053
1054/*!
1055 No longer in use.
1056 */
1057void Generator::generateOverload(const Node *node, CodeMarker *marker)
1058{
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);
1066}
1067
1068void Generator::generateReimplementedFrom(const FunctionNode *func,
1069 CodeMarker *marker)
1070{
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);
1081 }
1082 }
1083}
1084
1085const Atom *Generator::generateAtomList(const Atom *atom,
1086 const Node *relative,
1087 CodeMarker *marker,
1088 bool generate,
1089 int &numAtoms)
1090{
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;
1102
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;
1112 }
1113
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);
1124 }
1125 atom = atom->next();
1126 }
1127 }
1128 else if (atom->type() == Atom::FormatElse ||
1129 atom->type() == Atom::FormatEndif) {
1130 return atom;
1131 }
1132 else {
1133 int n = 1;
1134 if (generate) {
1135 n += generateAtom(atom, relative, marker);
1136 numAtoms += n;
1137 }
1138 while (n-- > 0)
1139 atom = atom->next();
1140 }
1141 }
1142 return 0;
1143}
1144
1145void Generator::appendFullName(Text& text,
1146 const Node *apparentNode,
1147 const Node *relative,
1148 CodeMarker *marker,
1149 const Node *actualNode)
1150{
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);
1157}
1158
1159void Generator::appendFullName(Text& text,
1160 const Node *apparentNode,
1161 const QString& fullName,
1162 const Node *actualNode)
1163{
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);
1170}
1171
1172void Generator::appendFullNames(Text& text,
1173 const NodeList& nodes,
1174 const Node* relative,
1175 CodeMarker* marker)
1176{
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;
1183 }
1184}
1185
1186void Generator::appendSortedNames(Text& text,
1187 const ClassNode *classe,
1188 const QList<RelatedClass> &classes,
1189 CodeMarker *marker)
1190{
1191 QList<RelatedClass>::ConstIterator r;
1192 QMap<QString,Text> classMap;
1193 int index = 0;
1194
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;
1203 }
1204 ++r;
1205 }
1206
1207 QStringList classNames = classMap.keys();
1208 classNames.sort();
1209
1210 foreach (const QString &className, classNames) {
1211 text << classMap[className];
1212 text << separator(index++, classNames.count());
1213 }
1214}
1215
1216int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
1217{
1218 int skipAhead = 0;
1219 atom = atom->next();
1220 while (atom != 0 && atom->type() != type) {
1221 skipAhead++;
1222 atom = atom->next();
1223 }
1224 return skipAhead;
1225}
1226
1227QString Generator::fullName(const Node *node,
1228 const Node *relative,
1229 CodeMarker *marker) const
1230{
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);
1238}
1239
1240QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.