tools/qdoc3/generator.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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 ** 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.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    59 QT_BEGIN_NAMESPACE
       
    60 
       
    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 QString Generator::outDir;
       
    69 QString Generator::project;
       
    70 
       
    71 static void singularPlural(Text& text, const NodeList& nodes)
       
    72 {
       
    73     if (nodes.count() == 1)
       
    74         text << " is";
       
    75     else
       
    76         text << " are";
       
    77 }
       
    78 
       
    79 Generator::Generator()
       
    80     : amp("&amp;"),
       
    81       lt("&lt;"),
       
    82       gt("&gt;"),
       
    83       quot("&quot;"),
       
    84       tag("</?@[^>]*>")
       
    85 {
       
    86     generators.prepend(this);
       
    87 }
       
    88 
       
    89 Generator::~Generator()
       
    90 {
       
    91     generators.removeAll(this);
       
    92 }
       
    93 
       
    94 void Generator::initializeGenerator(const Config & /* config */)
       
    95 {
       
    96 }
       
    97 
       
    98 void Generator::terminateGenerator()
       
    99 {
       
   100 }
       
   101 
       
   102 void Generator::initialize(const Config &config)
       
   103 {
       
   104     outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
       
   105     if (!outputFormats.isEmpty()) {
       
   106         outDir = config.getString(CONFIG_OUTPUTDIR);
       
   107         if (outDir.isEmpty())
       
   108             config.lastLocation().fatal(tr("No output directory specified in configuration file"));
       
   109 
       
   110         QDir dirInfo;
       
   111         if (dirInfo.exists(outDir)) {
       
   112             if (!Config::removeDirContents(outDir))
       
   113                 config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
       
   114         }
       
   115         else {
       
   116             if (!dirInfo.mkpath(outDir))
       
   117                 config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
       
   118         }
       
   119 
       
   120         if (!dirInfo.mkdir(outDir + "/images"))
       
   121             config.lastLocation().fatal(tr("Cannot create output directory '%1'")
       
   122                                         .arg(outDir + "/images"));
       
   123     }
       
   124 
       
   125     imageFiles = config.getStringList(CONFIG_IMAGES);
       
   126     imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
       
   127 
       
   128     QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
       
   129     QSet<QString> formats = config.subVars(imagesDotFileExtensions);
       
   130     QSet<QString>::ConstIterator f = formats.begin();
       
   131     while (f != formats.end()) {
       
   132         imgFileExts[*f] = config.getStringList(imagesDotFileExtensions +
       
   133                                                Config::dot + *f);
       
   134         ++f;
       
   135     }
       
   136 
       
   137     QList<Generator *>::ConstIterator g = generators.begin();
       
   138     while (g != generators.end()) {
       
   139         if (outputFormats.contains((*g)->format())) {
       
   140             (*g)->initializeGenerator(config);
       
   141             QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES +
       
   142                                                            Config::dot +
       
   143                                                            (*g)->format());
       
   144             QStringList::ConstIterator e = extraImages.begin();
       
   145             while (e != extraImages.end()) {
       
   146                 QString userFriendlyFilePath;
       
   147                 QString filePath = Config::findFile(config.lastLocation(),
       
   148                                                     imageFiles, imageDirs, *e,
       
   149                                                     imgFileExts[(*g)->format()],
       
   150                                                     userFriendlyFilePath);
       
   151                 if (!filePath.isEmpty())
       
   152                     Config::copyFile(config.lastLocation(),
       
   153                                      filePath,
       
   154                                      userFriendlyFilePath,
       
   155                                      (*g)->outputDir() +
       
   156                                      "/images");
       
   157                 ++e;
       
   158             }
       
   159         }
       
   160         ++g;
       
   161     }
       
   162 
       
   163     QRegExp secondParamAndAbove("[\2-\7]");
       
   164     QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
       
   165     QSet<QString>::ConstIterator n = formattingNames.begin();
       
   166     while (n != formattingNames.end()) {
       
   167         QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
       
   168 
       
   169         QSet<QString> formats = config.subVars(formattingDotName);
       
   170         QSet<QString>::ConstIterator f = formats.begin();
       
   171         while (f != formats.end()) {
       
   172             QString def = config.getString(formattingDotName +
       
   173                                            Config::dot + *f);
       
   174             if (!def.isEmpty()) {
       
   175                 int numParams = Config::numParams(def);
       
   176                 int numOccs = def.count("\1");
       
   177 
       
   178                 if (numParams != 1) {
       
   179                     config.lastLocation().warning(tr("Formatting '%1' must "
       
   180                                                      "have exactly one "
       
   181                                                      "parameter (found %2)")
       
   182                                                   .arg(*n).arg(numParams));
       
   183                 }
       
   184                 else if (numOccs > 1) {
       
   185                     config.lastLocation().fatal(tr("Formatting '%1' must "
       
   186                                                    "contain exactly one "
       
   187                                                    "occurrence of '\\1' "
       
   188                                                    "(found %2)")
       
   189                                                 .arg(*n).arg(numOccs));
       
   190                 }
       
   191                 else {
       
   192                     int paramPos = def.indexOf("\1");
       
   193                     fmtLeftMaps[*f].insert(*n, def.left(paramPos));
       
   194                     fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
       
   195                 }
       
   196             }
       
   197             ++f;
       
   198         }
       
   199         ++n;
       
   200     }
       
   201 
       
   202     project = config.getString(CONFIG_PROJECT);
       
   203 }
       
   204 
       
   205 void Generator::terminate()
       
   206 {
       
   207     QList<Generator *>::ConstIterator g = generators.begin();
       
   208     while (g != generators.end()) {
       
   209         if (outputFormats.contains((*g)->format()))
       
   210             (*g)->terminateGenerator();
       
   211         ++g;
       
   212     }
       
   213 
       
   214     fmtLeftMaps.clear();
       
   215     fmtRightMaps.clear();
       
   216     imgFileExts.clear();
       
   217     imageFiles.clear();
       
   218     imageDirs.clear();
       
   219     outDir = "";
       
   220 }
       
   221 
       
   222 Generator *Generator::generatorForFormat(const QString& format)
       
   223 {
       
   224     QList<Generator *>::ConstIterator g = generators.begin();
       
   225     while (g != generators.end()) {
       
   226         if ((*g)->format() == format)
       
   227             return *g;
       
   228         ++g;
       
   229     }
       
   230     return 0;
       
   231 }
       
   232 
       
   233 void Generator::startText(const Node * /* relative */,
       
   234                           CodeMarker * /* marker */)
       
   235 {
       
   236 }
       
   237 
       
   238 void Generator::endText(const Node * /* relative */,
       
   239                         CodeMarker * /* marker */)
       
   240 {
       
   241 }
       
   242 
       
   243 int Generator::generateAtom(const Atom * /* atom */,
       
   244                             const Node * /* relative */,
       
   245                             CodeMarker * /* marker */)
       
   246 {
       
   247     return 0;
       
   248 }
       
   249 
       
   250 void Generator::generateClassLikeNode(const InnerNode * /* classe */,
       
   251                                       CodeMarker * /* marker */)
       
   252 {
       
   253 }
       
   254 
       
   255 void Generator::generateFakeNode(const FakeNode * /* fake */,
       
   256                                  CodeMarker * /* marker */)
       
   257 {
       
   258 }
       
   259 
       
   260 bool Generator::generateText(const Text& text,
       
   261                              const Node *relative,
       
   262                              CodeMarker *marker)
       
   263 {
       
   264     if (text.firstAtom() != 0) {
       
   265         int numAtoms = 0;
       
   266         startText(relative, marker);
       
   267         generateAtomList(text.firstAtom(),
       
   268                          relative,
       
   269                          marker,
       
   270                          true,
       
   271                          numAtoms);
       
   272         endText(relative, marker);
       
   273         return true;
       
   274     }
       
   275     return false;
       
   276 }
       
   277 
       
   278 #ifdef QDOC_QML
       
   279 /*!
       
   280   Extract sections of markup text surrounded by \e qmltext
       
   281   and \e endqmltext and output them.
       
   282  */
       
   283 bool Generator::generateQmlText(const Text& text,
       
   284                                 const Node *relative,
       
   285                                 CodeMarker *marker,
       
   286                                 const QString& /* qmlName */ )
       
   287 {
       
   288     const Atom* atom = text.firstAtom();
       
   289     if (atom == 0)
       
   290         return false;
       
   291 
       
   292     startText(relative, marker);
       
   293     while (atom) {
       
   294         if (atom->type() != Atom::QmlText)
       
   295             atom = atom->next();
       
   296         else {
       
   297             atom = atom->next();
       
   298             while (atom && (atom->type() != Atom::EndQmlText)) {
       
   299                 int n = 1 + generateAtom(atom, relative, marker);
       
   300                 while (n-- > 0)
       
   301                     atom = atom->next();
       
   302             }
       
   303         }
       
   304     }
       
   305     endText(relative, marker);
       
   306     return true;
       
   307 }
       
   308 #endif
       
   309 
       
   310 void Generator::generateBody(const Node *node, CodeMarker *marker)
       
   311 {
       
   312     bool quiet = false;
       
   313 
       
   314     if (node->type() == Node::Function) {
       
   315 #if 0        
       
   316         const FunctionNode *func = (const FunctionNode *) node;
       
   317         if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
       
   318             generateOverload(node, marker);
       
   319 #endif        
       
   320     }
       
   321     else if (node->type() == Node::Fake) {
       
   322         const FakeNode *fake = static_cast<const FakeNode *>(node);
       
   323         if (fake->subType() == Node::Example)
       
   324             generateExampleFiles(fake, marker);
       
   325         else if (fake->subType() == Node::File)
       
   326             quiet = true;
       
   327     }
       
   328 
       
   329     if (node->doc().isEmpty()) {
       
   330         if (!quiet && !node->isReimp()) // ### might be unnecessary
       
   331             node->location().warning(tr("No documentation for '%1'")
       
   332                             .arg(marker->plainFullName(node)));
       
   333     }
       
   334     else {
       
   335         if (node->type() == Node::Function) {
       
   336             const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   337             if (func->reimplementedFrom() != 0)
       
   338                 generateReimplementedFrom(func, marker);
       
   339         }
       
   340         
       
   341         if (!generateText(node->doc().body(), node, marker))
       
   342             if (node->isReimp())
       
   343                 return;
       
   344 
       
   345         if (node->type() == Node::Enum) {
       
   346             const EnumNode *enume = (const EnumNode *) node;
       
   347 
       
   348             QSet<QString> definedItems;
       
   349             QList<EnumItem>::ConstIterator it = enume->items().begin();
       
   350             while (it != enume->items().end()) {
       
   351                 definedItems.insert((*it).name());
       
   352                 ++it;
       
   353             }
       
   354 
       
   355             QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
       
   356             QSet<QString> allItems = definedItems + documentedItems;
       
   357             if (allItems.count() > definedItems.count() ||
       
   358                  allItems.count() > documentedItems.count()) {
       
   359                 QSet<QString>::ConstIterator a = allItems.begin();
       
   360                 while (a != allItems.end()) {
       
   361                     if (!definedItems.contains(*a)) {
       
   362                         QString details;
       
   363                         QString best = nearestName(*a, definedItems);
       
   364                         if (!best.isEmpty() && !documentedItems.contains(best))
       
   365                             details = tr("Maybe you meant '%1'?").arg(best);
       
   366 
       
   367                         node->doc().location().warning(
       
   368                             tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
       
   369                             details);
       
   370                     }
       
   371                     else if (!documentedItems.contains(*a)) {
       
   372                         node->doc().location().warning(
       
   373                             tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
       
   374                     }
       
   375                     ++a;
       
   376                 }
       
   377             }
       
   378         }
       
   379         else if (node->type() == Node::Function) {
       
   380             const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   381             QSet<QString> definedParams;
       
   382             QList<Parameter>::ConstIterator p = func->parameters().begin();
       
   383             while (p != func->parameters().end()) {
       
   384                 if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
       
   385                         && func->name() != QLatin1String("operator++")
       
   386                         && func->name() != QLatin1String("operator--")) {
       
   387                     node->doc().location().warning(tr("Missing parameter name"));
       
   388                 }
       
   389                 else {
       
   390                     definedParams.insert((*p).name());
       
   391                 }
       
   392                 ++p;
       
   393             }
       
   394 
       
   395             QSet<QString> documentedParams = func->doc().parameterNames();
       
   396             QSet<QString> allParams = definedParams + documentedParams;
       
   397             if (allParams.count() > definedParams.count()
       
   398                     || allParams.count() > documentedParams.count()) {
       
   399                 QSet<QString>::ConstIterator a = allParams.begin();
       
   400                 while (a != allParams.end()) {
       
   401                     if (!definedParams.contains(*a)) {
       
   402                         QString details;
       
   403                         QString best = nearestName(*a, definedParams);
       
   404                         if (!best.isEmpty())
       
   405                             details = tr("Maybe you meant '%1'?").arg(best);
       
   406 
       
   407                         node->doc().location().warning(
       
   408                             tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
       
   409                             details);
       
   410                     }
       
   411                     else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
       
   412                         bool needWarning = (func->status() > Node::Obsolete);
       
   413                         if (func->overloadNumber() > 1) {
       
   414                             FunctionNode *primaryFunc =
       
   415                                     func->parent()->findFunctionNode(func->name());
       
   416                             if (primaryFunc) {
       
   417                                 foreach (const Parameter &param,
       
   418                                          primaryFunc->parameters()) {
       
   419                                     if (param.name() == *a) {
       
   420                                         needWarning = false;
       
   421                                         break;
       
   422                                     }
       
   423                                 }
       
   424                             }
       
   425                         }
       
   426                         if (needWarning && !func->isReimp())
       
   427                             node->doc().location().warning(
       
   428                                 tr("Undocumented parameter '%1' in %2")
       
   429                                 .arg(*a).arg(marker->plainFullName(node)));
       
   430                     }
       
   431                     ++a;
       
   432                 }
       
   433             }
       
   434 /* Something like this return value check should be implemented at some point. */
       
   435             if (func->status() > Node::Obsolete && func->returnType() == "bool"
       
   436                     && func->reimplementedFrom() == 0 && !func->isOverload()) {
       
   437                 QString body = func->doc().body().toString();
       
   438                 if (!body.contains("return", Qt::CaseInsensitive))
       
   439                     node->doc().location().warning(tr("Undocumented return value"));
       
   440             }
       
   441 #if 0
       
   442             // Now we put this at the top, before the other text.
       
   443             if (func->reimplementedFrom() != 0)
       
   444                 generateReimplementedFrom(func, marker);
       
   445 #endif            
       
   446         }
       
   447     }
       
   448 
       
   449     if (node->type() == Node::Fake) {
       
   450         const FakeNode *fake = static_cast<const FakeNode *>(node);
       
   451         if (fake->subType() == Node::File) {
       
   452             Text text;
       
   453             Quoter quoter;
       
   454             Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
       
   455             QString code = quoter.quoteTo(fake->location(), "", "");
       
   456             text << Atom(Atom::Code, code);
       
   457             generateText(text, fake, marker);
       
   458         }
       
   459     }
       
   460 }
       
   461 
       
   462 void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
       
   463 {
       
   464     QList<Text> alsoList = node->doc().alsoList();
       
   465     supplementAlsoList(node, alsoList);
       
   466 
       
   467     if (!alsoList.isEmpty()) {
       
   468         Text text;
       
   469         text << Atom::ParaLeft << "See also ";
       
   470 
       
   471         for (int i = 0; i < alsoList.size(); ++i)
       
   472             text << alsoList.at(i) << separator(i, alsoList.size());
       
   473 
       
   474         text << Atom::ParaRight;
       
   475         generateText(text, node, marker);
       
   476     }
       
   477 }
       
   478 
       
   479 void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
       
   480 {
       
   481     QList<RelatedClass>::ConstIterator r;
       
   482     int index;
       
   483 
       
   484     if (!classe->baseClasses().isEmpty()) {
       
   485         Text text;
       
   486         text << Atom::ParaLeft << "Inherits ";
       
   487 
       
   488         r = classe->baseClasses().begin();
       
   489         index = 0;
       
   490         while (r != classe->baseClasses().end()) {
       
   491             text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
       
   492                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   493                  << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
       
   494                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   495 
       
   496             if ((*r).access == Node::Protected) {
       
   497                 text << " (protected)";
       
   498             }
       
   499             else if ((*r).access == Node::Private) {
       
   500                 text << " (private)";
       
   501             }
       
   502             text << separator(index++, classe->baseClasses().count());
       
   503             ++r;
       
   504         }
       
   505         text << Atom::ParaRight;
       
   506         generateText(text, classe, marker);
       
   507     }
       
   508 }
       
   509 
       
   510 #ifdef QDOC_QML
       
   511 /*!
       
   512  */
       
   513 void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
       
   514 {
       
   515     // stub.
       
   516 }
       
   517 #endif
       
   518 
       
   519 void Generator::generateInheritedBy(const ClassNode *classe,
       
   520                                     CodeMarker *marker)
       
   521 {
       
   522     if (!classe->derivedClasses().isEmpty()) {
       
   523         Text text;
       
   524         text << Atom::ParaLeft << "Inherited by ";
       
   525 
       
   526         appendSortedNames(text, classe, classe->derivedClasses(), marker);
       
   527         text << Atom::ParaRight;
       
   528         generateText(text, classe, marker);
       
   529     }
       
   530 }
       
   531 
       
   532 void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
       
   533 {
       
   534     if (fake->childNodes().isEmpty())
       
   535         return;
       
   536 
       
   537     OpenedList openedList(OpenedList::Bullet);
       
   538 
       
   539     Text text;
       
   540     text << Atom::ParaLeft << "Files:" << Atom::ParaRight
       
   541          << Atom(Atom::ListLeft, openedList.styleString());
       
   542     foreach (const Node *child, fake->childNodes()) {
       
   543         QString exampleFile = child->name();
       
   544         openedList.next();
       
   545         text << Atom(Atom::ListItemNumber, openedList.numberString())
       
   546              << Atom(Atom::ListItemLeft, openedList.styleString())
       
   547              << Atom::ParaLeft
       
   548              << Atom(Atom::Link, exampleFile)
       
   549              << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   550              << exampleFile
       
   551              << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
       
   552              << Atom::ParaRight
       
   553              << Atom(Atom::ListItemRight, openedList.styleString());
       
   554     }
       
   555     text << Atom(Atom::ListRight, openedList.styleString());
       
   556     generateText(text, fake, marker);
       
   557 }
       
   558 
       
   559 void Generator::generateModuleWarning(const ClassNode *classe,
       
   560                                       CodeMarker *marker)
       
   561 {
       
   562     QString module = classe->moduleName();
       
   563     if (!module.isEmpty()) {
       
   564         Text text;
       
   565         if (!editionModuleMap["DesktopLight"].contains(module)) {
       
   566             text << Atom::ParaLeft
       
   567                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   568                  << "This class is not part of the Qt GUI Framework Edition."
       
   569                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   570                  << Atom::ParaRight;
       
   571         }
       
   572         else if (module == "Qt3Support") {
       
   573             text << Atom::ParaLeft
       
   574                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   575                  << "Note to Qt GUI Framework Edition users:"
       
   576                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   577                  << " This class is only available in the "
       
   578                  << Atom(Atom::AutoLink, "Qt Full Framework Edition")
       
   579                  << "." << Atom::ParaRight;
       
   580         }
       
   581 
       
   582         generateText(text, classe, marker);
       
   583     }
       
   584 }
       
   585 
       
   586 QString Generator::indent(int level, const QString& markedCode)
       
   587 {
       
   588     if (level == 0)
       
   589         return markedCode;
       
   590 
       
   591     QString t;
       
   592     int column = 0;
       
   593 
       
   594     int i = 0;
       
   595     while (i < (int) markedCode.length()) {
       
   596         if (markedCode.at(i) == QLatin1Char('<')) {
       
   597             while (i < (int) markedCode.length()) {
       
   598                 t += markedCode.at(i++);
       
   599                 if (markedCode.at(i - 1) == QLatin1Char('>'))
       
   600                     break;
       
   601             }
       
   602         }
       
   603         else {
       
   604             if (markedCode.at(i) == QLatin1Char('\n')) {
       
   605                 column = 0;
       
   606             }
       
   607             else {
       
   608                 if (column == 0) {
       
   609                     for (int j = 0; j < level; j++)
       
   610                         t += QLatin1Char(' ');
       
   611                 }
       
   612                 column++;
       
   613             }
       
   614             t += markedCode.at(i++);
       
   615         }
       
   616     }
       
   617     return t;
       
   618 }
       
   619 
       
   620 QString Generator::plainCode(const QString& markedCode)
       
   621 {
       
   622     QString t = markedCode;
       
   623     t.replace(tag, QString());
       
   624     t.replace(quot, QLatin1String("\""));
       
   625     t.replace(gt, QLatin1String(">"));
       
   626     t.replace(lt, QLatin1String("<"));
       
   627     t.replace(amp, QLatin1String("&"));
       
   628     return t;
       
   629 }
       
   630 
       
   631 QString Generator::typeString(const Node *node)
       
   632 {
       
   633     switch (node->type()) {
       
   634     case Node::Namespace:
       
   635         return "namespace";
       
   636     case Node::Class:
       
   637         return "class";
       
   638     case Node::Fake:
       
   639     default:
       
   640         return "documentation";
       
   641     case Node::Enum:
       
   642         return "enum";
       
   643     case Node::Typedef:
       
   644         return "typedef";
       
   645     case Node::Function:
       
   646         return "function";
       
   647     case Node::Property:
       
   648         return "property";
       
   649     }
       
   650 }
       
   651 
       
   652 QString Generator::imageFileName(const Node *relative, const QString& fileBase)
       
   653 {
       
   654     QString userFriendlyFilePath;
       
   655     QString filePath = Config::findFile(
       
   656         relative->doc().location(), imageFiles, imageDirs, fileBase,
       
   657         imgFileExts[format()], userFriendlyFilePath);
       
   658 
       
   659     if (filePath.isEmpty())
       
   660         return QString();
       
   661 
       
   662     return QLatin1String("images/")
       
   663            + Config::copyFile(relative->doc().location(),
       
   664                               filePath, userFriendlyFilePath,
       
   665                               outputDir() + QLatin1String("/images"));
       
   666 }
       
   667 
       
   668 void Generator::setImageFileExtensions(const QStringList& extensions)
       
   669 {
       
   670     imgFileExts[format()] = extensions;
       
   671 }
       
   672 
       
   673 void Generator::unknownAtom(const Atom *atom)
       
   674 {
       
   675     Location::internalError(tr("unknown atom type '%1' in %2 generator")
       
   676                             .arg(atom->typeString()).arg(format()));
       
   677 }
       
   678 
       
   679 bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
       
   680 {
       
   681     return atom->next() != 0 && atom->next()->type() == expectedAtomType;
       
   682 }
       
   683 
       
   684 void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
       
   685 {
       
   686     if (node->type() == Node::Function) {
       
   687         const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   688         if (func->overloadNumber() == 1) {
       
   689             QString alternateName;
       
   690             const FunctionNode *alternateFunc = 0;
       
   691 
       
   692             if (func->name().startsWith("set") && func->name().size() >= 4) {
       
   693                 alternateName = func->name()[3].toLower();
       
   694                 alternateName += func->name().mid(4);
       
   695                 alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   696 
       
   697                 if (!alternateFunc) {
       
   698                     alternateName = "is" + func->name().mid(3);
       
   699                     alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   700                     if (!alternateFunc) {
       
   701                         alternateName = "has" + func->name().mid(3);
       
   702                         alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   703                     }
       
   704                 }
       
   705             }
       
   706             else if (!func->name().isEmpty()) {
       
   707                 alternateName = "set";
       
   708                 alternateName += func->name()[0].toUpper();
       
   709                 alternateName += func->name().mid(1);
       
   710                 alternateFunc = func->parent()->findFunctionNode(alternateName);
       
   711             }
       
   712 
       
   713             if (alternateFunc && alternateFunc->access() != Node::Private) {
       
   714                 int i;
       
   715                 for (i = 0; i < alsoList.size(); ++i) {
       
   716                     if (alsoList.at(i).toString().contains(alternateName))
       
   717                         break;
       
   718                 }
       
   719 
       
   720                 if (i == alsoList.size()) {
       
   721                     alternateName += "()";
       
   722 
       
   723                     Text also;
       
   724                     also << Atom(Atom::Link, alternateName)
       
   725                          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   726                          << alternateName
       
   727                          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   728                     alsoList.prepend(also);
       
   729                 }
       
   730             }
       
   731         }
       
   732     }
       
   733 }
       
   734 
       
   735 QMap<QString, QString>& Generator::formattingLeftMap()
       
   736 {
       
   737     return fmtLeftMaps[format()];
       
   738 }
       
   739 
       
   740 QMap<QString, QString>& Generator::formattingRightMap()
       
   741 {
       
   742     return fmtRightMaps[format()];
       
   743 }
       
   744 
       
   745 QString Generator::trimmedTrailing(const QString &string)
       
   746 {
       
   747     QString trimmed = string;
       
   748     while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
       
   749         trimmed.truncate(trimmed.length() - 1);
       
   750     return trimmed;
       
   751 }
       
   752 
       
   753 void Generator::generateStatus(const Node *node, CodeMarker *marker)
       
   754 {
       
   755     Text text;
       
   756 
       
   757     switch (node->status()) {
       
   758     case Node::Commendable:
       
   759     case Node::Main:
       
   760         break;
       
   761     case Node::Preliminary:
       
   762         text << Atom::ParaLeft
       
   763              << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   764              << "This "
       
   765              << typeString(node)
       
   766              << " is under development and is subject to change."
       
   767              << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   768              << Atom::ParaRight;
       
   769         break;
       
   770     case Node::Deprecated:
       
   771         text << Atom::ParaLeft;
       
   772         if (node->isInnerNode())
       
   773             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
       
   774         text << "This " << typeString(node) << " is deprecated.";
       
   775         if (node->isInnerNode())
       
   776             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
       
   777         text << Atom::ParaRight;
       
   778         break;
       
   779     case Node::Obsolete:
       
   780         text << Atom::ParaLeft;
       
   781         if (node->isInnerNode())
       
   782             text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
       
   783         text << "This " << typeString(node) << " is obsolete.";
       
   784         if (node->isInnerNode())
       
   785             text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
       
   786         text << " It is provided to keep old source code working. "
       
   787              << "We strongly advise against "
       
   788              << "using it in new code." << Atom::ParaRight;
       
   789         break;
       
   790     case Node::Compat:
       
   791         // reimplemented in HtmlGenerator subclass
       
   792         if (node->isInnerNode()) {
       
   793             text << Atom::ParaLeft
       
   794                  << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
       
   795                  << "This "
       
   796                  << typeString(node)
       
   797                  << " is part of the Qt 3 compatibility layer."
       
   798                  << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
       
   799                  << " It is provided to keep old source code working. "
       
   800                  << "We strongly advise against "
       
   801                  << "using it in new code. See "
       
   802                  << Atom(Atom::AutoLink, "Porting to Qt 4")
       
   803                  << " for more information."
       
   804                  << Atom::ParaRight;
       
   805         }
       
   806         break;
       
   807     case Node::Internal:
       
   808     default:
       
   809         break;
       
   810     }
       
   811     generateText(text, node, marker);
       
   812 }
       
   813 
       
   814 void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
       
   815 {
       
   816     Text text;
       
   817     Text theStockLink;
       
   818     Node::ThreadSafeness threadSafeness = node->threadSafeness();
       
   819 
       
   820     Text rlink;
       
   821     rlink << Atom(Atom::Link,"reentrant")
       
   822           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   823           << "reentrant"
       
   824           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   825 
       
   826     Text tlink;
       
   827     tlink << Atom(Atom::Link,"thread-safe")
       
   828           << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
   829           << "thread-safe"
       
   830           << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   831 
       
   832     switch (threadSafeness) {
       
   833     case Node::UnspecifiedSafeness:
       
   834         break;
       
   835     case Node::NonReentrant:
       
   836         text << Atom::ParaLeft
       
   837              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
   838              << "Warning:"
       
   839              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
       
   840              << " This "
       
   841              << typeString(node)
       
   842              << " is not "
       
   843              << rlink
       
   844              << "."
       
   845              << Atom::ParaRight;
       
   846         break;
       
   847     case Node::Reentrant:
       
   848     case Node::ThreadSafe:
       
   849         text << Atom::ParaLeft
       
   850              << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
       
   851              << "Note:"
       
   852              << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD)
       
   853              << " ";
       
   854 
       
   855         if (node->isInnerNode()) {
       
   856             const InnerNode* innerNode = static_cast<const InnerNode*>(node);
       
   857             text << "All functions in this "
       
   858                  << typeString(node)
       
   859                  << " are ";
       
   860             if (threadSafeness == Node::ThreadSafe)
       
   861                 text << tlink;
       
   862             else
       
   863                 text << rlink;
       
   864 
       
   865             bool exceptions = false;
       
   866             NodeList reentrant;
       
   867             NodeList threadsafe;
       
   868             NodeList nonreentrant;
       
   869             NodeList::ConstIterator c = innerNode->childNodes().begin();
       
   870             while (c != innerNode->childNodes().end()) {
       
   871                 switch ((*c)->threadSafeness()) {
       
   872                 case Node::Reentrant:
       
   873                     reentrant.append(*c);
       
   874                     if (threadSafeness == Node::ThreadSafe)
       
   875                         exceptions = true;
       
   876                     break;
       
   877                 case Node::ThreadSafe:
       
   878                     threadsafe.append(*c);
       
   879                     if (threadSafeness == Node::Reentrant)
       
   880                         exceptions = true;
       
   881                     break;
       
   882                 case Node::NonReentrant:
       
   883                     nonreentrant.append(*c);
       
   884                     exceptions = true;
       
   885                     break;
       
   886                 default:
       
   887                     break;
       
   888                 }
       
   889                 ++c;
       
   890             }
       
   891             if (!exceptions) 
       
   892                 text << ".";
       
   893             else if (threadSafeness == Node::Reentrant) {
       
   894                 if (nonreentrant.isEmpty()) {
       
   895                     if (!threadsafe.isEmpty()) {
       
   896                         text << ", but ";
       
   897                         appendFullNames(text,threadsafe,innerNode,marker);
       
   898                         singularPlural(text,threadsafe);
       
   899                         text << " also " << tlink << ".";
       
   900                     }
       
   901                     else
       
   902                         text << ".";
       
   903                 }
       
   904                 else {
       
   905                     text << ", except for ";
       
   906                     appendFullNames(text,nonreentrant,innerNode,marker);
       
   907                     text << ", which";
       
   908                     singularPlural(text,nonreentrant);
       
   909                     text << " nonreentrant.";
       
   910                     if (!threadsafe.isEmpty()) {
       
   911                         text << " ";
       
   912                         appendFullNames(text,threadsafe,innerNode,marker);
       
   913                         singularPlural(text,threadsafe);
       
   914                         text << " " << tlink << ".";
       
   915                     }
       
   916                 }
       
   917             }
       
   918             else { // thread-safe
       
   919                 if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) {
       
   920                     text << ", except for ";
       
   921                     if (!reentrant.isEmpty()) {
       
   922                         appendFullNames(text,reentrant,innerNode,marker);
       
   923                         text << ", which";
       
   924                         singularPlural(text,reentrant);
       
   925                         text << " only " << rlink;
       
   926                         if (!nonreentrant.isEmpty())
       
   927                             text << ", and ";
       
   928                     }
       
   929                     if (!nonreentrant.isEmpty()) {
       
   930                         appendFullNames(text,nonreentrant,innerNode,marker);
       
   931                         text << ", which";
       
   932                         singularPlural(text,nonreentrant);
       
   933                         text << " nonreentrant.";
       
   934                     }
       
   935                     text << ".";
       
   936                 }
       
   937             }
       
   938         }
       
   939         else {
       
   940             text << "This " << typeString(node) << " is ";
       
   941             if (threadSafeness == Node::ThreadSafe)
       
   942                 text << tlink;
       
   943             else
       
   944                 text << rlink;
       
   945             text << ".";
       
   946         }
       
   947         text << Atom::ParaRight;
       
   948     }
       
   949     generateText(text,node,marker);
       
   950 }
       
   951 
       
   952 void Generator::generateSince(const Node *node, CodeMarker *marker)
       
   953 {
       
   954     if (!node->since().isEmpty()) {
       
   955         Text text;
       
   956         text << Atom::ParaLeft
       
   957              << "This "
       
   958              << typeString(node)
       
   959              << " was introduced in ";
       
   960         if (project.isEmpty())
       
   961              text << "version";
       
   962         else
       
   963              text << project;
       
   964         text << " " << node->since() << "." << Atom::ParaRight;
       
   965         generateText(text, node, marker);
       
   966     }
       
   967 }
       
   968 
       
   969 /*!
       
   970   No longer in use.
       
   971  */
       
   972 void Generator::generateOverload(const Node *node, CodeMarker *marker)
       
   973 {
       
   974     Text text;
       
   975     text << Atom::ParaLeft
       
   976          << "This function overloads ";
       
   977     QString t = node->name() + "()";
       
   978     text << Atom::AutoLink << t 
       
   979          << Atom::ParaRight;
       
   980     generateText(text, node, marker);
       
   981 }
       
   982 
       
   983 void Generator::generateReimplementedFrom(const FunctionNode *func,
       
   984                                           CodeMarker *marker)
       
   985 {
       
   986     if (func->reimplementedFrom() != 0) {
       
   987         const FunctionNode *from = func->reimplementedFrom();
       
   988         if (from->access() != Node::Private &&
       
   989             from->parent()->access() != Node::Private) {
       
   990             Text text;
       
   991             text << Atom::ParaLeft << "Reimplemented from ";
       
   992             QString fullName =  from->parent()->name() + "::" + from->name() + "()";
       
   993             appendFullName(text, from->parent(), fullName, from);
       
   994             text << "." << Atom::ParaRight;
       
   995             generateText(text, func, marker);
       
   996         }
       
   997     }
       
   998 }
       
   999 
       
  1000 const Atom *Generator::generateAtomList(const Atom *atom,
       
  1001                                         const Node *relative,
       
  1002                                         CodeMarker *marker,
       
  1003                                         bool generate,
       
  1004                                         int &numAtoms)
       
  1005 {
       
  1006     while (atom) {
       
  1007         if (atom->type() == Atom::FormatIf) {
       
  1008             int numAtoms0 = numAtoms;
       
  1009             bool rightFormat = canHandleFormat(atom->string());
       
  1010             atom = generateAtomList(atom->next(),
       
  1011                                     relative,
       
  1012                                     marker,
       
  1013                                     generate && rightFormat,
       
  1014                                     numAtoms);
       
  1015             if (!atom)
       
  1016                 return 0;
       
  1017 
       
  1018             if (atom->type() == Atom::FormatElse) {
       
  1019                 ++numAtoms;
       
  1020                 atom = generateAtomList(atom->next(),
       
  1021                                         relative,
       
  1022                                         marker,
       
  1023                                         generate && !rightFormat,
       
  1024                                         numAtoms);
       
  1025                 if (!atom)
       
  1026                     return 0;
       
  1027             }
       
  1028 
       
  1029             if (atom->type() == Atom::FormatEndif) {
       
  1030                 if (generate && numAtoms0 == numAtoms) {
       
  1031                     relative->location().warning(tr("Output format %1 not handled")
       
  1032                                                  .arg(format()));
       
  1033                     Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
       
  1034                     generateAtomList(&unhandledFormatAtom,
       
  1035                                      relative,
       
  1036                                      marker,
       
  1037                                      generate,
       
  1038                                      numAtoms);
       
  1039                 }
       
  1040                 atom = atom->next();
       
  1041             }
       
  1042         }
       
  1043         else if (atom->type() == Atom::FormatElse ||
       
  1044                  atom->type() == Atom::FormatEndif) {
       
  1045             return atom;
       
  1046         }
       
  1047         else {
       
  1048             int n = 1;
       
  1049             if (generate) {
       
  1050                 n += generateAtom(atom, relative, marker);
       
  1051                 numAtoms += n;
       
  1052             }
       
  1053             while (n-- > 0)
       
  1054                 atom = atom->next();
       
  1055         }
       
  1056     }
       
  1057     return 0;
       
  1058 }
       
  1059 
       
  1060 void Generator::appendFullName(Text& text,
       
  1061                                const Node *apparentNode,
       
  1062                                const Node *relative,
       
  1063                                CodeMarker *marker,
       
  1064                                const Node *actualNode)
       
  1065 {
       
  1066     if (actualNode == 0)
       
  1067         actualNode = apparentNode;
       
  1068     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
       
  1069          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  1070          << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
       
  1071          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  1072 }
       
  1073 
       
  1074 void Generator::appendFullName(Text& text,
       
  1075                                const Node *apparentNode,
       
  1076                                const QString& fullName,
       
  1077                                const Node *actualNode)
       
  1078 {
       
  1079     if (actualNode == 0)
       
  1080         actualNode = apparentNode;
       
  1081     text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
       
  1082          << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  1083          << Atom(Atom::String, fullName)
       
  1084          << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  1085 }
       
  1086 
       
  1087 void Generator::appendFullNames(Text& text,
       
  1088                                 const NodeList& nodes,
       
  1089                                 const Node* relative,
       
  1090                                 CodeMarker* marker)
       
  1091 {
       
  1092     NodeList::ConstIterator n = nodes.begin();
       
  1093     int index = 0;
       
  1094     while (n != nodes.end()) {
       
  1095         appendFullName(text,*n,relative,marker);
       
  1096         text << comma(index++,nodes.count());
       
  1097         ++n;
       
  1098     }
       
  1099 }
       
  1100 
       
  1101 void Generator::appendSortedNames(Text& text,
       
  1102                                   const ClassNode *classe,
       
  1103                                   const QList<RelatedClass> &classes,
       
  1104                                   CodeMarker *marker)
       
  1105 {
       
  1106     QList<RelatedClass>::ConstIterator r;
       
  1107     QMap<QString,Text> classMap;
       
  1108     int index = 0;
       
  1109 
       
  1110     r = classes.begin();
       
  1111     while (r != classes.end()) {
       
  1112         if ((*r).node->access() == Node::Public &&
       
  1113             (*r).node->status() != Node::Internal
       
  1114             && !(*r).node->doc().isEmpty()) {
       
  1115             Text className;
       
  1116             appendFullName(className, (*r).node, classe, marker);
       
  1117             classMap[className.toString().toLower()] = className;
       
  1118         }
       
  1119         ++r;
       
  1120     }
       
  1121 
       
  1122     QStringList classNames = classMap.keys();
       
  1123     classNames.sort();
       
  1124 
       
  1125     foreach (const QString &className, classNames) {
       
  1126         text << classMap[className];
       
  1127         text << separator(index++, classNames.count());
       
  1128     }
       
  1129 }
       
  1130 
       
  1131 int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
       
  1132 {
       
  1133     int skipAhead = 0;
       
  1134     atom = atom->next();
       
  1135     while (atom != 0 && atom->type() != type) {
       
  1136         skipAhead++;
       
  1137         atom = atom->next();
       
  1138     }
       
  1139     return skipAhead;
       
  1140 }
       
  1141 
       
  1142 QString Generator::fullName(const Node *node,
       
  1143                             const Node *relative,
       
  1144                             CodeMarker *marker) const
       
  1145 {
       
  1146     if (node->type() == Node::Fake)
       
  1147         return static_cast<const FakeNode *>(node)->title();
       
  1148     else if (node->type() == Node::Class &&
       
  1149         !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
       
  1150         return (static_cast<const ClassNode *>(node))->serviceName();
       
  1151     else
       
  1152         return marker->plainFullName(node, relative);
       
  1153 }
       
  1154 
       
  1155 QT_END_NAMESPACE