summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc/generator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qdoc/generator.cpp')
-rw-r--r--src/tools/qdoc/generator.cpp1909
1 files changed, 1117 insertions, 792 deletions
diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp
index c990413234..04624a5813 100644
--- a/src/tools/qdoc/generator.cpp
+++ b/src/tools/qdoc/generator.cpp
@@ -42,13 +42,11 @@
/*
generator.cpp
*/
-#include <qdir.h>
-#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
-#include <qdebug.h>
-#endif
-#include <qdebug.h>
+#include <QDir>
+#include <QDebug>
#include "codemarker.h"
#include "config.h"
+#include "ditaxmlgenerator.h"
#include "doc.h"
#include "editdistance.h"
#include "generator.h"
@@ -57,28 +55,25 @@
#include "quoter.h"
#include "separator.h"
#include "tokenizer.h"
-#include "ditaxmlgenerator.h"
+#include "tree.h"
QT_BEGIN_NAMESPACE
-QList<Generator *> Generator::generators;
+QString Generator::baseDir_;
+QStringList Generator::exampleDirs;
+QStringList Generator::exampleImgExts;
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;
+QList<Generator *> Generator::generators;
QStringList Generator::imageDirs;
-QStringList Generator::exampleDirs;
-QStringList Generator::exampleImgExts;
-QStringList Generator::scriptFiles;
-QStringList Generator::scriptDirs;
-QStringList Generator::styleFiles;
-QStringList Generator::styleDirs;
+QStringList Generator::imageFiles;
+QMap<QString, QStringList> Generator::imgFileExts;
QString Generator::outDir_;
-QString Generator::baseDir_;
-QString Generator::project;
+QSet<QString> Generator::outputFormats;
QHash<QString, QString> Generator::outputPrefixes;
-
+QString Generator::project;
+QStringList Generator::scriptDirs;
+QStringList Generator::scriptFiles;
QString Generator::sinceTitles[] =
{
" New Namespaces",
@@ -98,19 +93,14 @@ QString Generator::sinceTitles[] =
" New QML Methods",
""
};
+QStringList Generator::styleDirs;
+QStringList Generator::styleFiles;
-static void singularPlural(Text& text, const NodeList& nodes)
-{
- if (nodes.count() == 1)
- text << " is";
- else
- text << " are";
-}
Generator::Generator()
: amp("&amp;"),
- lt("&lt;"),
gt("&gt;"),
+ lt("&lt;"),
quot("&quot;"),
tag("</?@[^>]*>")
{
@@ -122,233 +112,371 @@ Generator::~Generator()
generators.removeAll(this);
}
-void Generator::initializeGenerator(const Config & /* config */)
+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::terminateGenerator()
+void Generator::appendFullName(Text& text,
+ const Node *apparentNode,
+ const QString& fullName,
+ const Node *actualNode)
{
+ if (actualNode == 0)
+ actualNode = apparentNode;
+ text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, fullName)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
}
-void Generator::initialize(const Config &config)
+void Generator::appendFullNames(Text& text,
+ const NodeList& nodes,
+ const Node* relative,
+ CodeMarker* marker)
{
- outputFormats = config.getOutputFormats();
- if (!outputFormats.isEmpty()) {
- outDir_ = config.getOutputDir();
- baseDir_ = config.getString(CONFIG_BASEDIR);
- if (!baseDir_.isEmpty())
- config.location().warning(tr("\"basedir\" specified in config file. "
- "All output will be in module directories of the output directory"));
- if (outDir_.isEmpty())
- config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line"));
+ NodeList::ConstIterator n = nodes.begin();
+ int index = 0;
+ while (n != nodes.end()) {
+ appendFullName(text,*n,relative,marker);
+ text << comma(index++,nodes.count());
+ ++n;
+ }
+}
- 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_));
+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;
+ }
- if (!dirInfo.mkdir(outDir_ + "/images"))
- config.lastLocation().fatal(tr("Cannot create output directory '%1'")
- .arg(outDir_ + "/images"));
- if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples"))
- config.lastLocation().fatal(tr("Cannot create output directory '%1'")
- .arg(outDir_ + "/images/used-in-examples"));
- if (!dirInfo.mkdir(outDir_ + "/scripts"))
- config.lastLocation().fatal(tr("Cannot create output directory '%1'")
- .arg(outDir_ + "/scripts"));
- if (!dirInfo.mkdir(outDir_ + "/style"))
- config.lastLocation().fatal(tr("Cannot create output directory '%1'")
- .arg(outDir_ + "/style"));
+ QStringList classNames = classMap.keys();
+ classNames.sort();
+
+ foreach (const QString &className, classNames) {
+ text << classMap[className];
+ text << separator(index++, classNames.count());
}
+}
- imageFiles = config.getCleanPathList(CONFIG_IMAGES);
- imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS);
- scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS);
- scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS);
- styleFiles = config.getCleanPathList(CONFIG_STYLES);
- styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS);
- exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
- exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot +
- CONFIG_IMAGEEXTENSIONS);
+void Generator::appendSortedQmlNames(Text& text,
+ const Node* base,
+ const NodeList& subs,
+ CodeMarker *marker)
+{
+ QMap<QString,Text> classMap;
+ int index = 0;
- 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;
+ for (int i = 0; i < subs.size(); ++i) {
+ Text t;
+ if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() ||
+ (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) {
+ appendFullName(t, subs[i], base, marker);
+ classMap[t.toString().toLower()] = t;
+ }
}
- QList<Generator *>::ConstIterator g = generators.begin();
- while (g != generators.end()) {
- if (outputFormats.contains((*g)->format())) {
- (*g)->initializeGenerator(config);
- QStringList extraImages =
- config.getCleanPathList(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;
- }
+ QStringList names = classMap.keys();
+ names.sort();
- // Documentation template handling
- QString templateDir = config.getString(
- (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR);
+ foreach (const QString &name, names) {
+ text << classMap[name];
+ text << separator(index++, names.count());
+ }
+}
- if (!templateDir.isEmpty()) {
- QStringList noExts;
- QStringList searchDirs = QStringList() << templateDir;
- QStringList scripts =
- config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS);
- e = scripts.begin();
- while (e != scripts.end()) {
- QString userFriendlyFilePath;
- QString filePath = Config::findFile(config.lastLocation(),
- scriptFiles,
- searchDirs,
- *e,
- noExts,
- userFriendlyFilePath);
- if (!filePath.isEmpty())
- Config::copyFile(config.lastLocation(),
- filePath,
- userFriendlyFilePath,
- (*g)->outputDir() +
- "/scripts");
- ++e;
- }
+/*!
+ Creates the file named \a fileName in the output directory.
+ Attaches a QTextStream to the created file, which is written
+ to all over the place using out().
+ */
+void Generator::beginSubPage(const InnerNode* node, const QString& fileName)
+{
+ QString path = outputDir() + QLatin1Char('/');
+ if (!node->outputSubdirectory().isEmpty())
+ path += node->outputSubdirectory() + QLatin1Char('/');
+ path += fileName;
+ QFile* outFile = new QFile(path);
+ if (!outFile->open(QFile::WriteOnly))
+ node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName()));
+ QTextStream* out = new QTextStream(outFile);
+
+ if (outputCodec)
+ out->setCodec(outputCodec);
+ outStreamStack.push(out);
+ const_cast<InnerNode*>(node)->setOutputFileName(fileName);
+}
- QStringList styles =
- config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS);
- e = styles.begin();
- while (e != styles.end()) {
- QString userFriendlyFilePath;
- QString filePath = Config::findFile(config.lastLocation(),
- styleFiles,
- searchDirs,
- *e,
- noExts,
- userFriendlyFilePath);
- if (!filePath.isEmpty())
- Config::copyFile(config.lastLocation(),
- filePath,
- userFriendlyFilePath,
- (*g)->outputDir() +
- "/style");
- ++e;
- }
- }
+/*!
+ Flush the text stream associated with the subpage, and
+ then pop it off the text stream stack and delete it.
+ This terminates output of the subpage.
+ */
+void Generator::endSubPage()
+{
+ outStreamStack.top()->flush();
+ delete outStreamStack.top()->device();
+ delete outStreamStack.pop();
+}
+
+void Generator::endText(const Node * /* relative */,
+ CodeMarker * /* marker */)
+{
+}
+
+QString Generator::fileBase(const Node *node) const
+{
+ if (node->relates())
+ node = node->relates();
+ else if (!node->isInnerNode())
+ node = node->parent();
+ if (node->subType() == Node::QmlPropertyGroup) {
+ node = node->parent();
+ }
+
+ QString base = node->doc().baseName();
+ if (!base.isEmpty())
+ return base;
+
+ const Node *p = node;
+
+ forever {
+ const Node *pp = p->parent();
+ base.prepend(p->name());
+ if (!p->qmlModuleIdentifier().isEmpty())
+ base.prepend(p->qmlModuleIdentifier()+QChar('-'));
+ /*
+ To avoid file name conflicts in the html directory,
+ we prepend a prefix (by default, "qml-") to the file name of QML
+ element doc files.
+ */
+ if ((p->subType() == Node::QmlClass) ||
+ (p->subType() == Node::QmlBasicType)) {
+ base.prepend(outputPrefix(QLatin1String("QML")));
}
- ++g;
+ if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake)
+ break;
+ base.prepend(QLatin1Char('-'));
+ p = pp;
+ }
+ if (node->type() == Node::Fake) {
+ if (node->subType() == Node::Collision) {
+ const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node);
+ if (ncn->currentChild())
+ return fileBase(ncn->currentChild());
+ base.prepend("collision-");
+ }
+ //Was QDOC2_COMPAT, required for index.html
+ if (base.endsWith(".html"))
+ base.truncate(base.length() - 5);
}
- 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;
+ // the code below is effectively equivalent to:
+ // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
+ // base = base.trimmed();
+ // base.replace(QLatin1Char(' '), QLatin1Char('-'));
+ // base = base.toLower();
+ // as this function accounted for ~8% of total running time
+ // we optimize a bit...
+
+ QString res;
+ // +5 prevents realloc in fileName() below
+ res.reserve(base.size() + 5);
+ bool begun = false;
+ for (int i = 0; i != base.size(); ++i) {
+ QChar c = base.at(i);
+ uint u = c.unicode();
+ if (u >= 'A' && u <= 'Z')
+ u -= 'A' - 'a';
+ if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
+ res += QLatin1Char(u);
+ begun = true;
+ }
+ else if (begun) {
+ res += QLatin1Char('-');
+ begun = false;
+ }
+ }
+ while (res.endsWith(QLatin1Char('-')))
+ res.chop(1);
+ return res;
+}
- 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 the \a node has a URL, return the URL as the file name.
+ Otherwise, construct the file name from the fileBase() and
+ the fileExtension(), and return the constructed name.
+ */
+QString Generator::fileName(const Node* node) const
+{
+ if (!node->url().isEmpty())
+ return node->url();
- if (numParams != 1) {
- config.lastLocation().warning(tr("Formatting '%1' must "
- "have exactly one "
- "parameter (found %2)")
- .arg(*n).arg(numParams));
+ QString name = fileBase(node);
+ name += QLatin1Char('.');
+ name += fileExtension(node);
+ return name;
+}
+
+/*!
+ For generating the "New Classes... in x.y" section on the
+ What's New in Qt x.y" page.
+ */
+void Generator::findAllSince(const InnerNode *node)
+{
+ NodeList::const_iterator child = node->childNodes().constBegin();
+
+ // Traverse the tree, starting at the node supplied.
+
+ while (child != node->childNodes().constEnd()) {
+
+ QString sinceString = (*child)->since();
+
+ if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
+
+ // Insert a new entry into each map for each new since string found.
+ NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString);
+ if (nsmap == newSinceMaps.end())
+ nsmap = newSinceMaps.insert(sinceString,NodeMultiMap());
+
+ NewClassMaps::iterator ncmap = newClassMaps.find(sinceString);
+ if (ncmap == newClassMaps.end())
+ ncmap = newClassMaps.insert(sinceString,NodeMap());
+
+ NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString);
+ if (nqcmap == newQmlClassMaps.end())
+ nqcmap = newQmlClassMaps.insert(sinceString,NodeMap());
+
+ if ((*child)->type() == Node::Function) {
+ // Insert functions into the general since map.
+ FunctionNode *func = static_cast<FunctionNode *>(*child);
+ if ((func->status() > Node::Obsolete) &&
+ (func->metaness() != FunctionNode::Ctor) &&
+ (func->metaness() != FunctionNode::Dtor)) {
+ nsmap.value().insert(func->name(),(*child));
}
- else if (numOccs > 1) {
- config.lastLocation().fatal(tr("Formatting '%1' must "
- "contain exactly one "
- "occurrence of '\\1' "
- "(found %2)")
- .arg(*n).arg(numOccs));
+ }
+ else if ((*child)->url().isEmpty()) {
+ if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
+ // Insert classes into the since and class maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() &&
+ (*child)->parent()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty())
+ className = (*child)->parent()->name()+"::"+className;
+
+ nsmap.value().insert(className,(*child));
+ ncmap.value().insert(className,(*child));
}
- else {
- int paramPos = def.indexOf("\1");
- fmtLeftMaps[*f].insert(*n, def.left(paramPos));
- fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1));
+ else if ((*child)->subType() == Node::QmlClass) {
+ // Insert QML elements into the since and element maps.
+ QString className = (*child)->name();
+ if ((*child)->parent() &&
+ (*child)->parent()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty())
+ className = (*child)->parent()->name()+"::"+className;
+
+ nsmap.value().insert(className,(*child));
+ nqcmap.value().insert(className,(*child));
+ }
+ else if ((*child)->type() == Node::QmlProperty) {
+ // Insert QML properties into the since map.
+ QString propertyName = (*child)->name();
+ nsmap.value().insert(propertyName,(*child));
}
}
- ++f;
- }
- ++n;
- }
+ else {
+ // Insert external documents into the general since map.
+ QString name = (*child)->name();
+ if ((*child)->parent() &&
+ (*child)->parent()->type() == Node::Namespace &&
+ !(*child)->parent()->name().isEmpty())
+ name = (*child)->parent()->name()+"::"+name;
- project = config.getString(CONFIG_PROJECT);
+ nsmap.value().insert(name,(*child));
+ }
- QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES);
- if (!prefixes.isEmpty()) {
- foreach (QString prefix, prefixes)
- outputPrefixes[prefix] = config.getString(
- CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
- } else
- outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
+ // Find child nodes with since commands.
+ if ((*child)->isInnerNode()) {
+ findAllSince(static_cast<InnerNode *>(*child));
+ }
+ }
+ ++child;
+ }
}
-void Generator::terminate()
+QMap<QString, QString>& Generator::formattingLeftMap()
{
- 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_ = "";
- QmlClassNode::terminate();
- ExampleNode::terminate();
+ return fmtLeftMaps[format()];
}
-Generator *Generator::generatorForFormat(const QString& format)
+QMap<QString, QString>& Generator::formattingRightMap()
{
- QList<Generator *>::ConstIterator g = generators.begin();
- while (g != generators.end()) {
- if ((*g)->format() == format)
- return *g;
- ++g;
- }
- return 0;
+ return fmtRightMaps[format()];
}
-void Generator::startText(const Node * /* relative */,
- CodeMarker * /* marker */)
+QString Generator::fullName(const Node *node,
+ const Node *relative,
+ CodeMarker *marker) const
{
+ if (node->type() == Node::Fake) {
+ const FakeNode* fn = static_cast<const FakeNode *>(node);
+#if 0
+ // Removed for QTBUG-22870
+ if (!fn->qmlModuleIdentifier().isEmpty())
+ return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title();
+#endif
+ return fn->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);
}
-void Generator::endText(const Node * /* relative */,
- CodeMarker * /* 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
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "See also "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ for (int i = 0; i < alsoList.size(); ++i)
+ text << alsoList.at(i) << separator(i, alsoList.size());
+
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
}
int Generator::generateAtom(const Atom * /* atom */,
@@ -358,68 +486,65 @@ int Generator::generateAtom(const Atom * /* atom */,
return 0;
}
-void Generator::generateClassLikeNode(const InnerNode * /* classe */,
- CodeMarker * /* marker */)
-{
-}
-
-void Generator::generateFakeNode(const FakeNode * /* fake */,
- CodeMarker * /* marker */)
-{
-}
-
-bool Generator::generateText(const Text& text,
- const Node *relative,
- CodeMarker *marker)
+const Atom *Generator::generateAtomList(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker,
+ bool generate,
+ int &numAtoms)
{
- bool result = false;
- if (text.firstAtom() != 0) {
- int numAtoms = 0;
- startText(relative, marker);
- generateAtomList(text.firstAtom(),
- relative,
- marker,
- true,
- numAtoms);
- endText(relative, marker);
- result = true;
- }
- return result;
-}
+ 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;
-#ifdef QDOC_QML
-/*!
- Extract sections of markup text surrounded by \e qmltext
- and \e endqmltext and output them.
- */
-bool Generator::generateQmlText(const Text& text,
- const Node *relative,
- CodeMarker *marker,
- const QString& /* qmlName */ )
-{
- const Atom* atom = text.firstAtom();
- bool result = false;
+ if (atom->type() == Atom::FormatElse) {
+ ++numAtoms;
+ atom = generateAtomList(atom->next(),
+ relative,
+ marker,
+ generate && !rightFormat,
+ numAtoms);
+ if (!atom)
+ return 0;
+ }
- if (atom != 0) {
- startText(relative, marker);
- 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();
+ if (atom->type() == Atom::FormatEndif) {
+ if (generate && numAtoms0 == numAtoms) {
+ relative->location().warning(tr("Output format %1 not handled %2")
+ .arg(format()).arg(outFileName()));
+ Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
+ generateAtomList(&unhandledFormatAtom,
+ relative,
+ marker,
+ generate,
+ numAtoms);
}
+ atom = atom->next();
}
}
- endText(relative, marker);
- result = true;
+ 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 result;
+ return 0;
}
-#endif
void Generator::generateBody(const Node *node, CodeMarker *marker)
{
@@ -568,105 +693,22 @@ void Generator::generateBody(const Node *node, CodeMarker *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
- << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
- << "See also "
- << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
-
- for (int i = 0; i < alsoList.size(); ++i)
- text << alsoList.at(i) << separator(i, alsoList.size());
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-/*!
- Generate a list of maintainers in the output
- */
-void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker)
-{
- QStringList sl = getMetadataElements(node,"maintainer");
-
- if (!sl.isEmpty()) {
- Text text;
- text << Atom::ParaLeft
- << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
- << "Maintained by: "
- << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
-
- for (int i = 0; i < sl.size(); ++i)
- text << sl.at(i) << separator(i, sl.size());
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
+void Generator::generateClassLikeNode(const InnerNode * /* classe */,
+ CodeMarker * /* marker */)
{
- QList<RelatedClass>::ConstIterator r;
- int index;
-
- if (!classe->baseClasses().isEmpty()) {
- Text text;
- text << Atom::ParaLeft
- << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
- << "Inherits: "
- << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
-
- 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);
- }
}
-#ifdef QDOC_QML
-/*!
- */
-void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
+void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
{
- // stub.
+ if (fake->childNodes().isEmpty())
+ return;
+ generateFileList(fake, marker, Node::File, QString("Files:"));
+ generateFileList(fake, marker, Node::Image, QString("Images:"));
}
-#endif
-void Generator::generateInheritedBy(const ClassNode *classe,
- CodeMarker *marker)
+void Generator::generateFakeNode(const FakeNode * /* fake */,
+ CodeMarker * /* marker */)
{
- if (!classe->derivedClasses().isEmpty()) {
- Text text;
- text << Atom::ParaLeft
- << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
- << "Inherited by: "
- << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
-
- appendSortedNames(text, classe, classe->derivedClasses(), marker);
- text << Atom::ParaRight;
- generateText(text, classe, marker);
- }
}
/*!
@@ -734,193 +776,250 @@ void Generator::generateFileList(const FakeNode* fake,
generateText(text, fake, marker);
}
-void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker)
+void Generator::generateInheritedBy(const ClassNode *classe,
+ CodeMarker *marker)
{
- if (fake->childNodes().isEmpty())
- return;
- generateFileList(fake, marker, Node::File, QString("Files:"));
- generateFileList(fake, marker, Node::Image, QString("Images:"));
+ if (!classe->derivedClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Inherited by: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
+
+ appendSortedNames(text, classe, classe->derivedClasses(), marker);
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
+ }
}
-QString Generator::indent(int level, const QString& markedCode)
+void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
{
- if (level == 0)
- return markedCode;
+ QList<RelatedClass>::ConstIterator r;
+ int index;
- QString t;
- int column = 0;
+ if (!classe->baseClasses().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Inherits: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
- int i = 0;
- while (i < (int) markedCode.length()) {
- if (markedCode.at(i) == QLatin1Char('\n')) {
- column = 0;
- }
- else {
- if (column == 0) {
- for (int j = 0; j < level; j++)
- t += QLatin1Char(' ');
+ 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)";
}
- column++;
+ else if ((*r).access == Node::Private) {
+ text << " (private)";
+ }
+ text << separator(index++, classe->baseClasses().count());
+ ++r;
}
- t += markedCode.at(i++);
+ text << Atom::ParaRight;
+ generateText(text, classe, marker);
}
- 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;
-}
+/*!
+ Recursive writing of HTML files from the root \a node.
-QString Generator::typeString(const Node *node)
+ \note NameCollisionNodes are skipped here and processed
+ later. See HtmlGenerator::generateDisambiguationPages()
+ for more on this.
+ */
+void Generator::generateInnerNode(const InnerNode* node)
{
- switch (node->type()) {
- case Node::Namespace:
- return "namespace";
- case Node::Class:
- return "class";
- case Node::Fake:
- {
- switch (node->subType()) {
- case Node::QmlClass:
- return "element";
- case Node::QmlPropertyGroup:
- return "property group";
- case Node::QmlBasicType:
- return "type";
- default:
- return "documentation";
+ if (!node->url().isNull())
+ return;
+
+ if (node->type() == Node::Fake) {
+ const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
+ if (fakeNode->subType() == Node::ExternalPage)
+ return;
+ if (fakeNode->subType() == Node::Image)
+ return;
+ if (fakeNode->subType() == Node::QmlPropertyGroup)
+ return;
+ if (fakeNode->subType() == Node::Page) {
+ if (node->count() > 0)
+ qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
}
}
- case Node::Enum:
- return "enum";
- case Node::Typedef:
- return "typedef";
- case Node::Function:
- return "function";
- case Node::Property:
- return "property";
- default:
- return "documentation";
+
+ /*
+ Obtain a code marker for the source file.
+ */
+ CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
+
+ if (node->parent() != 0) {
+ /*
+ Skip name collision nodes here and process them
+ later in generateDisambiguationPages(). Each one
+ is appended to a list for later.
+ */
+ if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) {
+ const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node);
+ collisionNodes.append(const_cast<NameCollisionNode*>(ncn));
+ }
+ else {
+ beginSubPage(node, fileName(node));
+ if (node->type() == Node::Namespace || node->type() == Node::Class) {
+ generateClassLikeNode(node, marker);
+ }
+ else if (node->type() == Node::Fake) {
+ generateFakeNode(static_cast<const FakeNode *>(node), marker);
+ }
+ endSubPage();
+ }
+ }
+
+ NodeList::ConstIterator c = node->childNodes().begin();
+ while (c != node->childNodes().end()) {
+ if ((*c)->isInnerNode() && (*c)->access() != Node::Private) {
+ generateInnerNode((const InnerNode *) *c);
+ }
+ ++c;
}
}
/*!
- Returns a relative path name for an image.
+ Generate a list of maintainers in the output
*/
-QString Generator::imageFileName(const Node *relative, const QString& fileBase)
+void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker)
{
- QString userFriendlyFilePath;
- QString filePath = Config::findFile(
- relative->doc().location(), imageFiles, imageDirs, fileBase,
- imgFileExts[format()], userFriendlyFilePath);
+ QStringList sl = getMetadataElements(node,"maintainer");
- if (filePath.isEmpty())
- return QString();
+ if (!sl.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD)
+ << "Maintained by: "
+ << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD);
- QString path = Config::copyFile(relative->doc().location(),
- filePath,
- userFriendlyFilePath,
- outputDir() + QLatin1String("/images"));
- QString images = "images";
- if (path[0] != '/')
- images.append(QLatin1Char('/'));
- return images + path;
-}
+ for (int i = 0; i < sl.size(); ++i)
+ text << sl.at(i) << separator(i, sl.size());
-void Generator::setImageFileExtensions(const QStringList& extensions)
-{
- imgFileExts[format()] = extensions;
+ text << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
}
-void Generator::unknownAtom(const Atom *atom)
+#ifdef QDOC_QML
+/*!
+ Output the "Inherit by" list for the QML element,
+ if it is inherited by any other elements.
+ */
+void Generator::generateQmlInheritedBy(const QmlClassNode* qcn,
+ CodeMarker* marker)
{
- Location::internalError(tr("unknown atom type '%1' in %2 generator")
- .arg(atom->typeString()).arg(format()));
+ if (qcn) {
+ NodeList subs;
+ QmlClassNode::subclasses(qcn->name(),subs);
+ if (!subs.isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft << "Inherited by ";
+ appendSortedQmlNames(text,qcn,subs,marker);
+ text << Atom::ParaRight;
+ generateText(text, qcn, marker);
+ }
+ }
}
+#endif
-bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
+#ifdef QDOC_QML
+/*!
+ */
+void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* )
{
- return atom->next() != 0 && atom->next()->type() == expectedAtomType;
+ // stub.
}
+#endif
-void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
+#ifdef QDOC_QML
+/*!
+ Extract sections of markup text surrounded by \e qmltext
+ and \e endqmltext and output them.
+ */
+bool Generator::generateQmlText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker,
+ const QString& /* qmlName */ )
{
- 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 += "()";
+ const Atom* atom = text.firstAtom();
+ bool result = false;
- Text also;
- also << Atom(Atom::Link, alternateName)
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << alternateName
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- alsoList.prepend(also);
+ if (atom != 0) {
+ startText(relative, marker);
+ 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);
+ result = true;
}
+ return result;
}
+#endif
-QMap<QString, QString>& Generator::formattingLeftMap()
+void Generator::generateReimplementedFrom(const FunctionNode *func,
+ CodeMarker *marker)
{
- return fmtLeftMaps[format()];
+ 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 ";
+ QString fullName = from->parent()->name() + "::" + from->name() + "()";
+ appendFullName(text, from->parent(), fullName, from);
+ text << "." << Atom::ParaRight;
+ generateText(text, func, marker);
+ }
+ }
}
-QMap<QString, QString>& Generator::formattingRightMap()
+void Generator::generateSince(const Node *node, CodeMarker *marker)
{
- return fmtRightMaps[format()];
-}
+ if (!node->since().isEmpty()) {
+ Text text;
+ text << Atom::ParaLeft
+ << "This "
+ << typeString(node);
+ if (node->type() == Node::Enum)
+ text << " was introduced or modified in ";
+ else
+ text << " was introduced in ";
-/*
- Trims trailimng whitespace off the \a string and returns
- the trimmed string.
- */
-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;
+ QStringList since = node->since().split(QLatin1Char(' '));
+ if (since.count() == 1) {
+ // Handle legacy use of \since <version>.
+ if (project.isEmpty())
+ text << "version";
+ else
+ text << project;
+ text << " " << since[0];
+ } else {
+ // Reconstruct the <project> <version> string.
+ text << " " << since.join(" ");
+ }
+
+ text << "." << Atom::ParaRight;
+ generateText(text, node, marker);
+ }
}
void Generator::generateStatus(const Node *node, CodeMarker *marker)
@@ -984,10 +1083,28 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker)
generateText(text, node, marker);
}
+bool Generator::generateText(const Text& text,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ bool result = false;
+ if (text.firstAtom() != 0) {
+ int numAtoms = 0;
+ startText(relative, marker);
+ generateAtomList(text.firstAtom(),
+ relative,
+ marker,
+ true,
+ numAtoms);
+ endText(relative, marker);
+ result = true;
+ }
+ return result;
+}
+
void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
{
Text text;
- Text theStockLink;
Node::ThreadSafeness threadSafeness = node->threadSafeness();
Text rlink;
@@ -1125,208 +1242,438 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
generateText(text,node,marker);
}
-void Generator::generateSince(const Node *node, CodeMarker *marker)
+/*!
+ This function is recursive.
+ */
+void Generator::generateTree(const Tree *tree)
{
- if (!node->since().isEmpty()) {
- Text text;
- text << Atom::ParaLeft
- << "This "
- << typeString(node);
- if (node->type() == Node::Enum)
- text << " was introduced or modified in ";
- else
- text << " was introduced in ";
+ generateInnerNode(tree->root());
+}
- QStringList since = node->since().split(QLatin1Char(' '));
- if (since.count() == 1) {
- // Handle legacy use of \since <version>.
- if (project.isEmpty())
- text << "version";
- else
- text << project;
- text << " " << since[0];
- } else {
- // Reconstruct the <project> <version> string.
- text << " " << since.join(" ");
- }
+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;
+}
- text << "." << Atom::ParaRight;
- generateText(text, node, marker);
+/*!
+ Looks up the tag \a t in the map of metadata values for the
+ current topic in \a inner. If a value for the tag is found,
+ the value is returned.
+
+ \note If \a t is found in the metadata map, it is erased.
+ i.e. Once you call this function for a particular \a t,
+ you consume \a t.
+ */
+QString Generator::getMetadataElement(const InnerNode* inner, const QString& t)
+{
+ QString s;
+ QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
+ QStringMultiMap::iterator i = metaTagMap.find(t);
+ if (i != metaTagMap.end()) {
+ s = i.value();
+ metaTagMap.erase(i);
}
+ return s;
}
-void Generator::generateReimplementedFrom(const FunctionNode *func,
- CodeMarker *marker)
+/*!
+ Looks up the tag \a t in the map of metadata values for the
+ current topic in \a inner. If values for the tag are found,
+ they are returned in a string list.
+
+ \note If \a t is found in the metadata map, all the pairs
+ having the key \a t are erased. i.e. Once you call this
+ function for a particular \a t, you consume \a t.
+ */
+QStringList Generator::getMetadataElements(const InnerNode* inner, const QString& t)
{
- 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 ";
- QString fullName = from->parent()->name() + "::" + from->name() + "()";
- appendFullName(text, from->parent(), fullName, from);
- text << "." << Atom::ParaRight;
- generateText(text, func, marker);
+ QStringList s;
+ QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
+ s = metaTagMap.values(t);
+ if (!s.isEmpty())
+ metaTagMap.remove(t);
+ return s;
+}
+
+/*!
+ Returns a relative path name for an image.
+ */
+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();
+
+ QString path = Config::copyFile(relative->doc().location(),
+ filePath,
+ userFriendlyFilePath,
+ outputDir() + QLatin1String("/images"));
+ QString images = "images";
+ if (path[0] != '/')
+ images.append(QLatin1Char('/'));
+ return images + path;
+}
+
+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('\n')) {
+ column = 0;
}
+ else {
+ if (column == 0) {
+ for (int j = 0; j < level; j++)
+ t += QLatin1Char(' ');
+ }
+ column++;
+ }
+ t += markedCode.at(i++);
}
+ return t;
}
-const Atom *Generator::generateAtomList(const Atom *atom,
- const Node *relative,
- CodeMarker *marker,
- bool generate,
- int &numAtoms)
+void Generator::initialize(const Config &config)
{
- 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;
+ outputFormats = config.getOutputFormats();
+ if (!outputFormats.isEmpty()) {
+ outDir_ = config.getOutputDir();
+ baseDir_ = config.getString(CONFIG_BASEDIR);
+ if (!baseDir_.isEmpty())
+ config.location().warning(tr("\"basedir\" specified in config file. "
+ "All output will be in module directories of the output directory"));
+ if (outDir_.isEmpty())
+ config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line"));
- if (atom->type() == Atom::FormatElse) {
- ++numAtoms;
- atom = generateAtomList(atom->next(),
- relative,
- marker,
- generate && !rightFormat,
- numAtoms);
- if (!atom)
- return 0;
+ 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"));
+ if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples"))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'")
+ .arg(outDir_ + "/images/used-in-examples"));
+ if (!dirInfo.mkdir(outDir_ + "/scripts"))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'")
+ .arg(outDir_ + "/scripts"));
+ if (!dirInfo.mkdir(outDir_ + "/style"))
+ config.lastLocation().fatal(tr("Cannot create output directory '%1'")
+ .arg(outDir_ + "/style"));
+ }
+
+ imageFiles = config.getCleanPathList(CONFIG_IMAGES);
+ imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS);
+ scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS);
+ scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS);
+ styleFiles = config.getCleanPathList(CONFIG_STYLES);
+ styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS);
+ exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
+ exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot +
+ CONFIG_IMAGEEXTENSIONS);
+
+ 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.getCleanPathList(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;
}
- if (atom->type() == Atom::FormatEndif) {
- if (generate && numAtoms0 == numAtoms) {
- relative->location().warning(tr("Output format %1 not handled %2")
- .arg(format()).arg(outFileName()));
- Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
- generateAtomList(&unhandledFormatAtom,
- relative,
- marker,
- generate,
- numAtoms);
+ // Documentation template handling
+ QString templateDir = config.getString(
+ (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR);
+
+ if (!templateDir.isEmpty()) {
+ QStringList noExts;
+ QStringList searchDirs = QStringList() << templateDir;
+ QStringList scripts =
+ config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS);
+ e = scripts.begin();
+ while (e != scripts.end()) {
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(config.lastLocation(),
+ scriptFiles,
+ searchDirs,
+ *e,
+ noExts,
+ userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(),
+ filePath,
+ userFriendlyFilePath,
+ (*g)->outputDir() +
+ "/scripts");
+ ++e;
+ }
+
+ QStringList styles =
+ config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS);
+ e = styles.begin();
+ while (e != styles.end()) {
+ QString userFriendlyFilePath;
+ QString filePath = Config::findFile(config.lastLocation(),
+ styleFiles,
+ searchDirs,
+ *e,
+ noExts,
+ userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ Config::copyFile(config.lastLocation(),
+ filePath,
+ userFriendlyFilePath,
+ (*g)->outputDir() +
+ "/style");
+ ++e;
}
- 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;
+ ++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));
+ }
}
- while (n-- > 0)
- atom = atom->next();
+ ++f;
}
+ ++n;
}
- return 0;
+
+ project = config.getString(CONFIG_PROJECT);
+
+ QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES);
+ if (!prefixes.isEmpty()) {
+ foreach (QString prefix, prefixes)
+ outputPrefixes[prefix] = config.getString(
+ CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
+ } else
+ outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
}
-void Generator::appendFullName(Text& text,
- const Node *apparentNode,
- const Node *relative,
- CodeMarker *marker,
- const Node *actualNode)
+void Generator::initializeGenerator(const Config & /* config */)
{
- 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::appendFullName(Text& text,
- const Node *apparentNode,
- const QString& fullName,
- const Node *actualNode)
+bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
{
- if (actualNode == 0)
- actualNode = apparentNode;
- text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, fullName)
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ return atom->next() != 0 && atom->next()->type() == expectedAtomType;
}
-void Generator::appendFullNames(Text& text,
- const NodeList& nodes,
- const Node* relative,
- CodeMarker* marker)
+/*!
+ Used for writing to the current output stream. Returns a
+ reference to the current output stream, which is then used
+ with the \c {<<} operator for writing.
+ */
+QTextStream &Generator::out()
{
- NodeList::ConstIterator n = nodes.begin();
- int index = 0;
- while (n != nodes.end()) {
- appendFullName(text,*n,relative,marker);
- text << comma(index++,nodes.count());
- ++n;
- }
+ return *outStreamStack.top();
}
-void Generator::appendSortedNames(Text& text,
- const ClassNode *classe,
- const QList<RelatedClass> &classes,
- CodeMarker *marker)
+QString Generator::outputPrefix(const QString &nodeType)
{
- QList<RelatedClass>::ConstIterator r;
- QMap<QString,Text> classMap;
- int index = 0;
+ return outputPrefixes[nodeType];
+}
- 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;
+bool Generator::parseArg(const QString& src,
+ const QString& tag,
+ int* pos,
+ int n,
+ QStringRef* contents,
+ QStringRef* par1,
+ bool debug)
+{
+#define SKIP_CHAR(c) \
+ if (debug) \
+ qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
+ if (i >= n || src[i] != c) { \
+ if (debug) \
+ qDebug() << " char '" << c << "' not found"; \
+ return false; \
+} \
+ ++i;
+
+
+#define SKIP_SPACE \
+ while (i < n && src[i] == ' ') \
+ ++i;
+
+ int i = *pos;
+ int j = i;
+
+ // assume "<@" has been parsed outside
+ //SKIP_CHAR('<');
+ //SKIP_CHAR('@');
+
+ if (tag != QStringRef(&src, i, tag.length())) {
+ if (0 && debug)
+ qDebug() << "tag " << tag << " not found at " << i;
+ return false;
+ }
+
+ if (debug)
+ qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
+
+ // skip tag
+ i += tag.length();
+
+ // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
+ if (par1) {
+ SKIP_SPACE;
+ // read parameter name
+ j = i;
+ while (i < n && src[i].isLetter())
+ ++i;
+ if (src[i] == '=') {
+ if (debug)
+ qDebug() << "read parameter" << QString(src.data() + j, i - j);
+ SKIP_CHAR('=');
+ SKIP_CHAR('"');
+ // skip parameter name
+ j = i;
+ while (i < n && src[i] != '"')
+ ++i;
+ *par1 = QStringRef(&src, j, i - j);
+ SKIP_CHAR('"');
+ SKIP_SPACE;
+ } else {
+ if (debug)
+ qDebug() << "no optional parameter found";
}
- ++r;
+ }
+ SKIP_SPACE;
+ SKIP_CHAR('>');
+
+ // find contents up to closing "</@tag>
+ j = i;
+ for (; true; ++i) {
+ if (i + 4 + tag.length() > n)
+ return false;
+ if (src[i] != '<')
+ continue;
+ if (src[i + 1] != '/')
+ continue;
+ if (src[i + 2] != '@')
+ continue;
+ if (tag != QStringRef(&src, i + 3, tag.length()))
+ continue;
+ if (src[i + 3 + tag.length()] != '>')
+ continue;
+ break;
}
- QStringList classNames = classMap.keys();
- classNames.sort();
+ *contents = QStringRef(&src, j, i - j);
- foreach (const QString &className, classNames) {
- text << classMap[className];
- text << separator(index++, classNames.count());
- }
+ i += tag.length() + 4;
+
+ *pos = i;
+ if (debug)
+ qDebug() << " tag " << tag << " found: pos now: " << i;
+ return true;
+#undef SKIP_CHAR
}
-void Generator::appendSortedQmlNames(Text& text,
- const Node* base,
- const NodeList& subs,
- CodeMarker *marker)
+QString Generator::plainCode(const QString& markedCode)
{
- QMap<QString,Text> classMap;
- int index = 0;
-
- for (int i = 0; i < subs.size(); ++i) {
- Text t;
- if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() ||
- (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) {
- appendFullName(t, subs[i], base, marker);
- classMap[t.toString().toLower()] = t;
- }
- }
+ 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;
+}
- QStringList names = classMap.keys();
- names.sort();
+void Generator::setImageFileExtensions(const QStringList& extensions)
+{
+ imgFileExts[format()] = extensions;
+}
- foreach (const QString &name, names) {
- text << classMap[name];
- text << separator(index++, names.count());
- }
+void Generator::singularPlural(Text& text, const NodeList& nodes)
+{
+ if (nodes.count() == 1)
+ text << " is";
+ else
+ text << " are";
}
int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
@@ -1340,156 +1687,134 @@ int Generator::skipAtoms(const Atom *atom, Atom::Type type) const
return skipAhead;
}
-QString Generator::fullName(const Node *node,
- const Node *relative,
- CodeMarker *marker) const
+void Generator::startText(const Node * /* relative */,
+ CodeMarker * /* marker */)
{
- if (node->type() == Node::Fake) {
- const FakeNode* fn = static_cast<const FakeNode *>(node);
-#if 0
- // Removed for QTBUG-22870
- if (!fn->qmlModuleIdentifier().isEmpty())
- return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title();
-#endif
- return fn->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);
}
-QString Generator::outputPrefix(const QString &nodeType)
+void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
{
- return outputPrefixes[nodeType];
-}
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast<const FunctionNode *>(node);
+ if (func->overloadNumber() == 1) {
+ QString alternateName;
+ const FunctionNode *alternateFunc = 0;
-/*!
- Looks up the tag \a t in the map of metadata values for the
- current topic in \a inner. If a value for the tag is found,
- the value is returned.
+ if (func->name().startsWith("set") && func->name().size() >= 4) {
+ alternateName = func->name()[3].toLower();
+ alternateName += func->name().mid(4);
+ alternateFunc = func->parent()->findFunctionNode(alternateName);
- \note If \a t is found in the metadata map, it is erased.
- i.e. Once you call this function for a particular \a t,
- you consume \a t.
- */
-QString Generator::getMetadataElement(const InnerNode* inner, const QString& t)
-{
- QString s;
- QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
- QStringMultiMap::iterator i = metaTagMap.find(t);
- if (i != metaTagMap.end()) {
- s = i.value();
- metaTagMap.erase(i);
+ 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);
+ }
+ }
+ }
}
- return s;
}
-/*!
- Looks up the tag \a t in the map of metadata values for the
- current topic in \a inner. If values for the tag are found,
- they are returned in a string list.
+void Generator::terminate()
+{
+ QList<Generator *>::ConstIterator g = generators.begin();
+ while (g != generators.end()) {
+ if (outputFormats.contains((*g)->format()))
+ (*g)->terminateGenerator();
+ ++g;
+ }
- \note If \a t is found in the metadata map, all the pairs
- having the key \a t are erased. i.e. Once you call this
- function for a particular \a t, you consume \a t.
- */
-QStringList Generator::getMetadataElements(const InnerNode* inner, const QString& t)
+ fmtLeftMaps.clear();
+ fmtRightMaps.clear();
+ imgFileExts.clear();
+ imageFiles.clear();
+ imageDirs.clear();
+ outDir_ = "";
+ QmlClassNode::terminate();
+ ExampleNode::terminate();
+}
+
+void Generator::terminateGenerator()
{
- QStringList s;
- QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap());
- s = metaTagMap.values(t);
- if (!s.isEmpty())
- metaTagMap.remove(t);
- return s;
}
/*!
- For generating the "New Classes... in 4.6" section on the
- What's New in 4.6" page.
+ Trims trailing whitespace off the \a string and returns
+ the trimmed string.
*/
-void Generator::findAllSince(const InnerNode *node)
+QString Generator::trimmedTrailing(const QString& string)
{
- NodeList::const_iterator child = node->childNodes().constBegin();
-
- // Traverse the tree, starting at the node supplied.
-
- while (child != node->childNodes().constEnd()) {
-
- QString sinceString = (*child)->since();
-
- if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) {
-
- // Insert a new entry into each map for each new since string found.
- NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString);
- if (nsmap == newSinceMaps.end())
- nsmap = newSinceMaps.insert(sinceString,NodeMultiMap());
-
- NewClassMaps::iterator ncmap = newClassMaps.find(sinceString);
- if (ncmap == newClassMaps.end())
- ncmap = newClassMaps.insert(sinceString,NodeMap());
-
- NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString);
- if (nqcmap == newQmlClassMaps.end())
- nqcmap = newQmlClassMaps.insert(sinceString,NodeMap());
-
- if ((*child)->type() == Node::Function) {
- // Insert functions into the general since map.
- FunctionNode *func = static_cast<FunctionNode *>(*child);
- if ((func->status() > Node::Obsolete) &&
- (func->metaness() != FunctionNode::Ctor) &&
- (func->metaness() != FunctionNode::Dtor)) {
- nsmap.value().insert(func->name(),(*child));
- }
- }
- else if ((*child)->url().isEmpty()) {
- if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) {
- // Insert classes into the since and class maps.
- QString className = (*child)->name();
- if ((*child)->parent() &&
- (*child)->parent()->type() == Node::Namespace &&
- !(*child)->parent()->name().isEmpty())
- className = (*child)->parent()->name()+"::"+className;
-
- nsmap.value().insert(className,(*child));
- ncmap.value().insert(className,(*child));
- }
- else if ((*child)->subType() == Node::QmlClass) {
- // Insert QML elements into the since and element maps.
- QString className = (*child)->name();
- if ((*child)->parent() &&
- (*child)->parent()->type() == Node::Namespace &&
- !(*child)->parent()->name().isEmpty())
- className = (*child)->parent()->name()+"::"+className;
-
- nsmap.value().insert(className,(*child));
- nqcmap.value().insert(className,(*child));
- }
- else if ((*child)->type() == Node::QmlProperty) {
- // Insert QML properties into the since map.
- QString propertyName = (*child)->name();
- nsmap.value().insert(propertyName,(*child));
- }
- }
- else {
- // Insert external documents into the general since map.
- QString name = (*child)->name();
- if ((*child)->parent() &&
- (*child)->parent()->type() == Node::Namespace &&
- !(*child)->parent()->name().isEmpty())
- name = (*child)->parent()->name()+"::"+name;
-
- nsmap.value().insert(name,(*child));
- }
+ QString trimmed = string;
+ while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
+ trimmed.truncate(trimmed.length() - 1);
+ return trimmed;
+}
- // Find child nodes with since commands.
- if ((*child)->isInnerNode()) {
- findAllSince(static_cast<InnerNode *>(*child));
- }
+QString Generator::typeString(const Node *node)
+{
+ switch (node->type()) {
+ case Node::Namespace:
+ return "namespace";
+ case Node::Class:
+ return "class";
+ case Node::Fake:
+ {
+ switch (node->subType()) {
+ case Node::QmlClass:
+ return "element";
+ case Node::QmlPropertyGroup:
+ return "property group";
+ case Node::QmlBasicType:
+ return "type";
+ default:
+ return "documentation";
}
- ++child;
}
+ case Node::Enum:
+ return "enum";
+ case Node::Typedef:
+ return "typedef";
+ case Node::Function:
+ return "function";
+ case Node::Property:
+ return "property";
+ default:
+ return "documentation";
+ }
+}
+
+void Generator::unknownAtom(const Atom *atom)
+{
+ Location::internalError(tr("unknown atom type '%1' in %2 generator")
+ .arg(atom->typeString()).arg(format()));
}
QT_END_NAMESPACE