summaryrefslogtreecommitdiffstats
path: root/tools/qdoc3/generator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qdoc3/generator.cpp')
-rw-r--r--tools/qdoc3/generator.cpp995
1 files changed, 995 insertions, 0 deletions
diff --git a/tools/qdoc3/generator.cpp b/tools/qdoc3/generator.cpp
new file mode 100644
index 0000000..d89d6af
--- /dev/null
+++ b/tools/qdoc3/generator.cpp
@@ -0,0 +1,995 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ generator.cpp
+*/
+
+#include <qdir.h>
+
+#include "codemarker.h"
+#include "config.h"
+#include "doc.h"
+#include "editdistance.h"
+#include "generator.h"
+#include "node.h"
+#include "openedlist.h"
+#include "quoter.h"
+#include "separator.h"
+#include "tokenizer.h"
+
+QT_BEGIN_NAMESPACE
+
+QList<Generator *> Generator::generators;
+QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps;
+QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps;
+QMap<QString, QStringList> Generator::imgFileExts;
+QSet<QString> Generator::outputFormats;
+QStringList Generator::imageFiles;
+QStringList Generator::imageDirs;
+QString Generator::outDir;
+QString Generator::project;
+
+static Text stockLink(const QString &target)
+{
+ return Text() << Atom(Atom::Link, target) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << target << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+Generator::Generator()
+ : amp("&amp;"), lt("&lt;"), gt("&gt;"), quot("&quot;"), tag("</?@[^>]*>")
+{
+ generators.prepend(this);
+}
+
+Generator::~Generator()
+{
+ generators.removeAll(this);
+}
+
+void Generator::initializeGenerator(const Config & /* config */)
+{
+}
+
+void Generator::terminateGenerator()
+{
+}
+
+void Generator::initialize(const Config &config)
+{
+ outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS);
+ if (!outputFormats.isEmpty()) {
+ outDir = config.getString(CONFIG_OUTPUTDIR);
+ if (outDir.isEmpty())
+ config.lastLocation().fatal(tr("No output directory specified in configuration file"));
+
+ QDir dirInfo;
+ if (dirInfo.exists(outDir)) {
+ if (!Config::removeDirContents(outDir))
+ config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir));
+ }
+ else {
+ if (!dirInfo.mkpath(outDir))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir));
+ }
+
+ if (!dirInfo.mkdir(outDir + "/images"))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'")
+ .arg(outDir + "/images"));
+ }
+
+ imageFiles = config.getStringList(CONFIG_IMAGES);
+ imageDirs = config.getStringList(CONFIG_IMAGEDIRS);
+
+ QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
+ QSet<QString> formats = config.subVars(imagesDotFileExtensions);
+ QSet<QString>::ConstIterator f = formats.begin();
+ while (f != formats.end()) {
+ imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f);
+ ++f;
+ }
+
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if (outputFormats.contains((*g)->format())) {
+ (*g)->initializeGenerator(config);
+ QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES + Config::dot
+ + (*g)->format());
+ QStringList::ConstIterator e = extraImages.begin();
+ while (e != extraImages.end()) {
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(config.lastLocation(), imageFiles, imageDirs, *e,
+ imgFileExts[(*g)->format()], userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(), filePath, userFriendlyFilePath,
+ (*g)->outputDir() + "/images");
+ ++e;
+ }
+ }
+ ++g;
+ }
+
+ QRegExp secondParamAndAbove("[\2-\7]");
+ QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING);
+ QSet<QString>::ConstIterator n = formattingNames.begin();
+ while (n != formattingNames.end()) {
+ QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n;
+
+ QSet<QString> formats = config.subVars(formattingDotName);
+ QSet<QString>::ConstIterator f = formats.begin();
+ while (f != formats.end()) {
+ QString def = config.getString(formattingDotName + Config::dot +
+ *f);
+ if (!def.isEmpty()) {
+ int numParams = Config::numParams(def);
+ int numOccs = def.count("\1");
+
+ if (numParams != 1) {
+ config.lastLocation().warning(tr("Formatting '%1' must have exactly one"
+ " parameter (found %2)")
+ .arg(*n).arg(numParams));
+ }
+ else if (numOccs > 1) {
+ config.lastLocation().fatal(tr("Formatting '%1' must contain exactly one"
+ " occurrence of '\\1' (found %2)")
+ .arg(*n).arg(numOccs));
+ }
+ else {
+ int paramPos = def.indexOf("\1");
+ fmtLeftMaps[*f].insert(*n, def.left(paramPos));
+ fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
+ }
+ }
+ ++f;
+ }
+ ++n;
+ }
+
+ project = config.getString(CONFIG_PROJECT);
+}
+
+void Generator::terminate()
+{
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if (outputFormats.contains((*g)->format()))
+ (*g)->terminateGenerator();
+ ++g;
+ }
+
+ fmtLeftMaps.clear();
+ fmtRightMaps.clear();
+ imgFileExts.clear();
+ imageFiles.clear();
+ imageDirs.clear();
+ outDir = "";
+}
+
+Generator *Generator::generatorForFormat(const QString& format)
+{
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if ((*g)->format() == format)
+ return *g;
+ ++g;
+ }
+ return 0;
+}
+
+void Generator::startText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::endText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+}
+
+int Generator::generateAtom(const Atom * /* atom */,
+ const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+ return 0;
+}
+
+void Generator::generateClassLikeNode(const InnerNode * /* classe */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::generateFakeNode(const FakeNode * /* fake */,
+ CodeMarker * /* marker */)
+{
+}
+
+void Generator::generateText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ if (text.firstAtom() != 0) {
+ int numAtoms = 0;
+ startText(relative, marker);
+ generateAtomList(text.firstAtom(),
+ relative,
+ marker,
+ true,
+ numAtoms);
+ endText(relative, marker);
+ }
+}
+
+#ifdef QDOC_QML
+void Generator::generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ if (text.firstAtom() != 0) {
+ startText(relative, marker);
+ const Atom *atom = text.firstAtom();
+ while (atom) {
+ if (atom->type() != Atom::QmlText)
+ atom = atom->next();
+ else {
+ atom = atom->next();
+ while (atom && (atom->type() != Atom::EndQmlText)) {
+ int n = 1 + generateAtom(atom, relative, marker);
+ while (n-- > 0)
+ atom = atom->next();
+ }
+ }
+ }
+ endText(relative, marker);
+ }
+}
+#endif
+
+void Generator::generateBody(const Node *node, CodeMarker *marker)
+{
+ bool quiet = false;
+
+ if (node->type() == Node::Function) {
+#if 0
+ const FunctionNode *func = (const FunctionNode *) node;
+ if (func->isOverload() && func->metaness() != FunctionNode::Ctor)
+ generateOverload(node, marker);
+#endif
+ }
+ else if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ if (fake->subType() == FakeNode::Example)
+ generateExampleFiles(fake, marker);
+ else if (fake->subType() == FakeNode::File)
+ quiet = true;
+ }
+
+ if (node->doc().isEmpty()) {
+ if (!quiet) // ### might be unnecessary
+ node->location().warning(tr("No documentation for '%1'")
+ .arg(marker->plainFullName(node)));
+ }
+ else {
+ generateText(node->doc().body(), node, marker);
+
+ if (node->type() == Node::Enum) {
+ const EnumNode *enume = (const EnumNode *) node;
+
+ QSet<QString> definedItems;
+ QList<EnumItem>::ConstIterator it = enume->items().begin();
+ while (it != enume->items().end()) {
+ definedItems.insert((*it).name());
+ ++it;
+ }
+
+ QSet<QString> documentedItems = enume->doc().enumItemNames().toSet();
+ QSet<QString> allItems = definedItems + documentedItems;
+ if (allItems.count() > definedItems.count() ||
+ allItems.count() > documentedItems.count()) {
+ QSet<QString>::ConstIterator a = allItems.begin();
+ while (a != allItems.end()) {
+ if (!definedItems.contains(*a)) {
+ QString details;
+ QString best = nearestName(*a, definedItems);
+ if (!best.isEmpty() && !documentedItems.contains(best))
+ details = tr("Maybe you meant '%1'?").arg(best);
+
+ node->doc().location().warning(
+ tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
+ details);
+ }
+ else if (!documentedItems.contains(*a)) {
+ node->doc().location().warning(
+ tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
+ }
+ ++a;
+ }
+ }
+ }
+ else if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+
+ QSet<QString> definedParams;
+ QList<Parameter>::ConstIterator p = func->parameters().begin();
+ while (p != func->parameters().end()) {
+ if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...")
+ && func->name() != QLatin1String("operator++")
+ && func->name() != QLatin1String("operator--")) {
+ node->doc().location().warning(tr("Missing parameter name"));
+ }
+ else {
+ definedParams.insert((*p).name());
+ }
+ ++p;
+ }
+
+ QSet<QString> documentedParams = func->doc().parameterNames();
+ QSet<QString> allParams = definedParams + documentedParams;
+ if (allParams.count() > definedParams.count()
+ || allParams.count() > documentedParams.count()) {
+ QSet<QString>::ConstIterator a = allParams.begin();
+ while (a != allParams.end()) {
+ if (!definedParams.contains(*a)) {
+ QString details;
+ QString best = nearestName(*a, definedParams);
+ if (!best.isEmpty())
+ details = tr("Maybe you meant '%1'?").arg(best);
+
+ node->doc().location().warning(
+ tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)),
+ details);
+ }
+ else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
+ bool needWarning = (func->status() > Node::Obsolete);
+ if (func->overloadNumber() > 1) {
+ FunctionNode *primaryFunc =
+ func->parent()->findFunctionNode(func->name());
+ if (primaryFunc) {
+ foreach (const Parameter &param, primaryFunc->parameters()) {
+ if (param.name() == *a) {
+ needWarning = false;
+ break;
+ }
+ }
+ }
+ }
+ if (needWarning)
+ node->doc().location().warning(
+ tr("Undocumented parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)));
+ }
+ ++a;
+ }
+ }
+/* Something like this return value check should be implemented at some point. */
+ if (func->status() > Node::Obsolete && func->returnType() == "bool"
+ && func->reimplementedFrom() == 0 && !func->isOverload()) {
+ QString body = func->doc().body().toString();
+ if (!body.contains("return", Qt::CaseInsensitive))
+ node->doc().location().warning(tr("Undocumented return value"));
+ }
+
+ if (func->reimplementedFrom() != 0)
+ generateReimplementedFrom(func, marker);
+ }
+ }
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fake = static_cast<const FakeNode *>(node);
+ if (fake->subType() == FakeNode::File) {
+ Text text;
+ Quoter quoter;
+ Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
+ QString code = quoter.quoteTo(fake->location(), "", "");
+ text << Atom(Atom::Code, code);
+ generateText(text, fake, marker);
+ }
+ }
+}
+
+void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
+{
+ QList<Text> alsoList = node->doc().alsoList();
+ supplementAlsoList(node, alsoList);
+
+ if (!alsoList.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "See also ";
+
+ for (int i = 0; i < alsoList.size(); ++i)
+ text << alsoList.at(i) << separator(i, alsoList.size());
+
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
+{
+ QList<RelatedClass>::ConstIterator r;
+ int index;
+
+ if (!classe->baseClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherits ";
+
+ r = classe->baseClasses().begin();
+ index = 0;
+ while (r != classe->baseClasses().end()) {
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, (*r).dataTypeWithTemplateArgs)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+
+ if ((*r).access == Node::Protected) {
+ text << " (protected)";
+ } else if ((*r).access == Node::Private) {
+ text << " (private)";
+ }
+ text << separator(index++, classe->baseClasses().count());
+ ++r;
+ }
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
+}
+
+void Generator::generateInheritedBy(const ClassNode *classe,
+ CodeMarker *marker)
+{
+ if (!classe->derivedClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherited by ";
+
+ appendSortedNames(text, classe, classe->derivedClasses(), marker);
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
+}
+
+void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
+{
+ if (fake->childNodes().isEmpty())
+ return;
+
+ OpenedList openedList(OpenedList::Bullet);
+
+ Text text;
+ text << Atom::ParaLeft << "Files:" << Atom::ParaRight
+ << Atom(Atom::ListLeft, openedList.styleString());
+ foreach (const Node *child, fake->childNodes()) {
+ QString exampleFile = child->name();
+ openedList.next();
+ text << Atom(Atom::ListItemNumber, openedList.numberString())
+ << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
+ << Atom(Atom::Link, exampleFile)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << exampleFile
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight << Atom(Atom::ListItemRight, openedList.styleString());
+ }
+ text << Atom(Atom::ListRight, openedList.styleString());
+ generateText(text, fake, marker);
+}
+
+void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marker)
+{
+ QString module = classe->moduleName();
+ if (!module.isEmpty()) {
+ Text text;
+ if (Tokenizer::isTrue("defined(consoleedition)")
+ && !editionModuleMap["Console"].contains(module)) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "This class is not part of the Qt Console Edition."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom::ParaRight;
+ }
+ else if (Tokenizer::isTrue("defined(desktoplightedition)")
+ && !editionModuleMap["DesktopLight"].contains(module)) {
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "This class is not part of the Qt Desktop Light Edition."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << Atom::ParaRight;
+ }
+ else if (module == "Qt3Support" && Tokenizer::isTrue("defined(opensourceedition)")) {
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
+ << "Note to Qt Desktop Light Edition users:"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " This class is only available in the "
+ << Atom(Atom::AutoLink, "Qt Desktop Edition")
+ << "." << Atom::ParaRight;
+ }
+
+ generateText(text, classe, marker);
+ }
+}
+
+QString Generator::indent(int level, const QString& markedCode)
+{
+ if (level == 0)
+ return markedCode;
+
+ QString t;
+ int column = 0;
+
+ int i = 0;
+ while (i < (int) markedCode.length()) {
+ if (markedCode.at(i) == QLatin1Char('<')) {
+ while (i < (int) markedCode.length()) {
+ t += markedCode.at(i++);
+ if (markedCode.at(i - 1) == QLatin1Char('>'))
+ break;
+ }
+ } else {
+ if (markedCode.at(i) == QLatin1Char('\n')) {
+ column = 0;
+ } else {
+ if (column == 0) {
+ for (int j = 0; j < level; j++)
+ t += QLatin1Char(' ');
+ }
+ column++;
+ }
+ t += markedCode.at(i++);
+ }
+ }
+ return t;
+}
+
+QString Generator::plainCode(const QString& markedCode)
+{
+ QString t = markedCode;
+ t.replace(tag, QString());
+ t.replace(quot, QLatin1String("\""));
+ t.replace(gt, QLatin1String(">"));
+ t.replace(lt, QLatin1String("<"));
+ t.replace(amp, QLatin1String("&"));
+ return t;
+}
+
+QString Generator::typeString(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ case Node::Class:
+ return "class";
+ case Node::Fake:
+ default:
+ return "documentation";
+ case Node::Enum:
+ return "enum";
+ case Node::Typedef:
+ return "typedef";
+ case Node::Function:
+ return "function";
+ case Node::Property:
+ return "property";
+ }
+}
+
+QString Generator::imageFileName(const Node *relative, const QString& fileBase)
+{
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(
+ relative->doc().location(), imageFiles, imageDirs, fileBase,
+ imgFileExts[format()], userFriendlyFilePath);
+
+ if (filePath.isEmpty())
+ return QString();
+
+ return QLatin1String("images/")
+ + Config::copyFile(relative->doc().location(),
+ filePath, userFriendlyFilePath,
+ outputDir() + QLatin1String("/images"));
+}
+
+void Generator::setImageFileExtensions(const QStringList& extensions)
+{
+ imgFileExts[format()] = extensions;
+}
+
+void Generator::unknownAtom(const Atom *atom)
+{
+ Location::internalError(tr("unknown atom type '%1' in %2 generator")
+ .arg(atom->typeString()).arg(format()));
+}
+
+bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
+{
+ return atom->next() != 0 && atom->next()->type() == expectedAtomType;
+}
+
+void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
+{
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->overloadNumber() == 1) {
+ QString alternateName;
+ const FunctionNode *alternateFunc = 0;
+
+ if (func->name().startsWith("set") && func->name().size() >= 4) {
+ alternateName = func->name()[3].toLower();
+ alternateName += func->name().mid(4);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+
+ if (!alternateFunc) {
+ alternateName = "is" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ if (!alternateFunc) {
+ alternateName = "has" + func->name().mid(3);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ }
+ }
+ } else if (!func->name().isEmpty()) {
+ alternateName = "set";
+ alternateName += func->name()[0].toUpper();
+ alternateName += func->name().mid(1);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
+ }
+
+ if (alternateFunc && alternateFunc->access() != Node::Private) {
+ int i;
+ for (i = 0; i < alsoList.size(); ++i) {
+ if (alsoList.at(i).toString().contains(alternateName))
+ break;
+ }
+
+ if (i == alsoList.size()) {
+ alternateName += "()";
+
+ Text also;
+ also << Atom(Atom::Link, alternateName)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << alternateName
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ alsoList.prepend(also);
+ }
+ }
+ }
+ }
+}
+
+QMap<QString, QString>& Generator::formattingLeftMap()
+{
+ return fmtLeftMaps[format()];
+}
+
+QMap<QString, QString>& Generator::formattingRightMap()
+{
+ return fmtRightMaps[format()];
+}
+
+QString Generator::trimmedTrailing(const QString &string)
+{
+ QString trimmed = string;
+ while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
+ trimmed.truncate(trimmed.length() - 1);
+ return trimmed;
+}
+
+void Generator::generateStatus(const Node *node, CodeMarker *marker)
+{
+ Text text;
+
+ switch (node->status()) {
+ case Node::Commendable:
+ case Node::Main:
+ break;
+ case Node::Preliminary:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
+ << typeString(node) << " is under development and is subject to change."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight;
+ break;
+ case Node::Deprecated:
+ text << Atom::ParaLeft;
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is deprecated.";
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ text << Atom::ParaRight;
+ break;
+ case Node::Obsolete:
+ text << Atom::ParaLeft;
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ text << "This " << typeString(node) << " is obsolete.";
+ if (node->isInnerNode())
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
+ text << " It is provided to keep old source code working. We strongly advise against "
+ << "using it in new code." << Atom::ParaRight;
+ break;
+ case Node::Compat:
+ // reimplemented in HtmlGenerator subclass
+ if (node->isInnerNode()) {
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
+ << typeString(node) << " is part of the Qt 3 compatibility layer."
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " It is provided to keep old source code working. We strongly advise against "
+ << "using it in new code. See "
+ << Atom(Atom::AutoLink, "Porting to Qt 4")
+ << " for more information."
+ << Atom::ParaRight;
+ }
+ break;
+ case Node::Internal:
+ default:
+ break;
+ }
+ generateText(text, node, marker);
+}
+
+void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
+{
+ Text text;
+ Text theStockLink;
+ Node::ThreadSafeness parent = node->parent()->inheritedThreadSafeness();
+
+ switch (node->threadSafeness()) {
+ case Node::UnspecifiedSafeness:
+ break;
+ case Node::NonReentrant:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "Warning:"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " This "
+ << typeString(node) << " is not " << stockLink("reentrant") << "." << Atom::ParaRight;
+ break;
+ case Node::Reentrant:
+ case Node::ThreadSafe:
+ text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
+ if (parent == Node::ThreadSafe) {
+ text << "Warning:";
+ } else {
+ text << "Note:";
+ }
+ text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " ";
+
+ if (node->threadSafeness() == Node::ThreadSafe)
+ theStockLink = stockLink("thread-safe");
+ else
+ theStockLink = stockLink("reentrant");
+
+ if (node->isInnerNode()) {
+ const InnerNode *innerNode = static_cast<const InnerNode *>(node);
+ text << "All the functions in this " << typeString(node) << " are "
+ << theStockLink;
+
+ NodeList except;
+ NodeList::ConstIterator c = innerNode->childNodes().begin();
+ while (c != innerNode->childNodes().end()) {
+ if ((*c)->threadSafeness() != Node::UnspecifiedSafeness)
+ except.append(*c);
+ ++c;
+ }
+ if (except.isEmpty()) {
+ text << ".";
+ }
+ else {
+ text << ", except ";
+
+ NodeList::ConstIterator e = except.begin();
+ int index = 0;
+ while (e != except.end()) {
+ appendFullName(text, *e, innerNode, marker);
+ text << separator(index++, except.count());
+ ++e;
+ }
+ }
+ }
+ else {
+ text << "This " << typeString(node) << " is " << theStockLink << ".";
+ }
+ text << Atom::ParaRight;
+ }
+ generateText(text, node, marker);
+}
+
+void Generator::generateSince(const Node *node, CodeMarker *marker)
+{
+ if (!node->since().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "This " << typeString(node)
+ << " was introduced in ";
+ if (project.isEmpty())
+ text << "version";
+ else
+ text << project;
+ text << " " << node->since() << "." << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
+}
+
+/*!
+ No longer in use.
+ */
+void Generator::generateOverload(const Node *node, CodeMarker *marker)
+{
+ Text text;
+ text << Atom::ParaLeft
+ << "This function overloads ";
+ QString t = node->name() + "()";
+ text << Atom::AutoLink << t
+ << Atom::ParaRight;
+ generateText(text, node, marker);
+}
+
+void Generator::generateReimplementedFrom(const FunctionNode *func,
+ CodeMarker *marker)
+{
+ if (func->reimplementedFrom() != 0) {
+ const FunctionNode *from = func->reimplementedFrom();
+ if (from->access() != Node::Private && from->parent()->access() != Node::Private) {
+ Text text;
+ text << Atom::ParaLeft << "Reimplemented from ";
+ appendFullName(text, from->parent(), func, marker, from);
+ text << "." << Atom::ParaRight;
+ generateText(text, func, marker);
+ }
+ }
+}
+
+const Atom *Generator::generateAtomList(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ bool generate,
+ int &numAtoms)
+{
+ while (atom) {
+ if (atom->type() == Atom::FormatIf) {
+ int numAtoms0 = numAtoms;
+ bool rightFormat = canHandleFormat(atom->string());
+ atom = generateAtomList(atom->next(),
+ relative,
+ marker,
+ generate && rightFormat,
+ numAtoms);
+ if (!atom)
+ return 0;
+
+ if (atom->type() == Atom::FormatElse) {
+ ++numAtoms;
+ atom = generateAtomList(atom->next(),
+ relative,
+ marker,
+ generate && !rightFormat,
+ numAtoms);
+ if (!atom)
+ return 0;
+ }
+
+ if (atom->type() == Atom::FormatEndif) {
+ if (generate && numAtoms0 == numAtoms) {
+ relative->location().warning(tr("Output format %1 not handled").
+ arg(format()));
+ Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
+ generateAtomList(&unhandledFormatAtom,
+ relative,
+ marker,
+ generate,
+ numAtoms);
+ }
+ atom = atom->next();
+ }
+ }
+ else if (atom->type() == Atom::FormatElse || atom->type() == Atom::FormatEndif) {
+ return atom;
+ }
+ else {
+ int n = 1;
+ if (generate) {
+ n += generateAtom(atom, relative, marker);
+ numAtoms += n;
+ }
+ while (n-- > 0)
+ atom = atom->next();
+ }
+ }
+ return 0;
+}
+
+void Generator::appendFullName(Text& text,
+ const Node *apparentNode,
+ const Node *relative,
+ CodeMarker *marker,
+ const Node *actualNode)
+{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, marker->plainFullName(apparentNode, relative))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+}
+
+void Generator::appendSortedNames(Text& text,
+ const ClassNode *classe,
+ const QList<RelatedClass> &classes,
+ CodeMarker *marker)
+{
+ QList<RelatedClass>::ConstIterator r;
+ QMap<QString,Text> classMap;
+ int index = 0;
+
+ r = classes.begin();
+ while (r != classes.end()) {
+ if ((*r).node->access() == Node::Public && (*r).node->status() != Node::Internal
+ && !(*r).node->doc().isEmpty()) {
+ Text className;
+ appendFullName(className, (*r).node, classe, marker);
+ classMap[className.toString().toLower()] = className;
+ }
+ ++r;
+ }
+
+ QStringList classNames = classMap.keys();
+ classNames.sort();
+
+ foreach (const QString &className, classNames) {
+ text << classMap[className];
+ text << separator(index++, classNames.count());
+ }
+}
+
+int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
+{
+ int skipAhead = 0;
+ atom = atom->next();
+ while (atom != 0 && atom->type() != type) {
+ skipAhead++;
+ atom = atom->next();
+ }
+ return skipAhead;
+}
+
+QString Generator::fullName(const Node *node,
+ const Node *relative,
+ CodeMarker *marker) const
+{
+ if (node->type() == Node::Fake)
+ return static_cast<const FakeNode *>(node)->title();
+ else if (node->type() == Node::Class &&
+ !(static_cast<const ClassNode *>(node))->serviceName().isEmpty())
+ return (static_cast<const ClassNode *>(node))->serviceName();
+ else
+ return marker->plainFullName(node, relative);
+}
+
+QT_END_NAMESPACE