From 5939cd33af5e43308e533a85444da073add8f05a Mon Sep 17 00:00:00 2001 From: Casper van Donderen Date: Sat, 17 Mar 2012 19:44:49 +0100 Subject: [PATCH] qdoc: Merge PageGenerator into Generator class. This change also sorts all functions in Generator by alphabet and moves the implementation of the GenerateQmlInheritedBy function to the Generator class, since the implementation in both the DITA and HTML generators was the same. Task-number: QTBUG-24833 Change-Id: I44588079159e03b7ff7549e5478babb1aabdaf1b Reviewed-by: Casper van Donderen --- src/tools/qdoc/ditaxmlgenerator.cpp | 42 +- src/tools/qdoc/ditaxmlgenerator.h | 5 +- src/tools/qdoc/generator.cpp | 2507 +++++++++++++++------------ src/tools/qdoc/generator.h | 184 +- src/tools/qdoc/htmlgenerator.cpp | 35 +- src/tools/qdoc/htmlgenerator.h | 9 +- src/tools/qdoc/pagegenerator.cpp | 389 ----- src/tools/qdoc/pagegenerator.h | 99 -- src/tools/qdoc/qdoc.pro | 2 - 9 files changed, 1540 insertions(+), 1732 deletions(-) delete mode 100644 src/tools/qdoc/pagegenerator.cpp delete mode 100644 src/tools/qdoc/pagegenerator.h diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index f19f902c6b..9cb50a44f7 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -43,6 +43,11 @@ ditaxmlgenerator.cpp */ +#include +#include +#include +#include +#include #include "codemarker.h" #include "codeparser.h" #include "ditaxmlgenerator.h" @@ -51,11 +56,6 @@ #include "separator.h" #include "tree.h" #include -#include -#include -#include -#include -#include QT_BEGIN_NAMESPACE @@ -636,7 +636,7 @@ void DitaXmlGenerator::generateTree(const Tree *tree) findAllNamespaces(tree->root()); findAllSince(tree->root()); - PageGenerator::generateTree(tree); + Generator::generateTree(tree); writeDitaMap(tree); } @@ -3816,7 +3816,7 @@ QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outp QString DitaXmlGenerator::fileBase(const Node* node) const { QString result; - result = PageGenerator::fileBase(node); + result = Generator::fileBase(node); #if 0 if (!node->isInnerNode()) { switch (node->status()) { @@ -3898,7 +3898,7 @@ QString DitaXmlGenerator::fileName(const Node* node) if (static_cast(node)->subType() == Node::Image) return node->name(); } - return PageGenerator::fileName(node); + return Generator::fileName(node); } QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative) @@ -4162,7 +4162,7 @@ const Node* DitaXmlGenerator::findNodeForTarget(const QString& target, const QPair DitaXmlGenerator::anchorForNode(const Node* node) { QPair anchorPair; - anchorPair.first = PageGenerator::fileName(node); + anchorPair.first = Generator::fileName(node); if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast(node); anchorPair.second = fakeNode->title(); @@ -4593,26 +4593,6 @@ void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* } } -/*! - Output the "Inherit by" list for the QML element, - if it is inherited by any other elements. - */ -void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, - CodeMarker* marker) -{ - 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); - } - } -} - /*! Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" line for the QML element, if there should be one. @@ -5559,7 +5539,7 @@ void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList void DitaXmlGenerator::beginSubPage(const InnerNode* node, const QString& fileName) { - PageGenerator::beginSubPage(node,fileName); + Generator::beginSubPage(node,fileName); (void) lookupGuidMap(fileName); QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); xmlWriterStack.push(writer); @@ -5580,7 +5560,7 @@ void DitaXmlGenerator::endSubPage() qDebug() << "Missing in" << outFileName() << sectionNestingLevel; xmlWriter().writeEndDocument(); delete xmlWriterStack.pop(); - PageGenerator::endSubPage(); + Generator::endSubPage(); } /*! diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index b3da02bce1..c2bd309200 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -47,14 +47,14 @@ #include #include "codemarker.h" #include "config.h" -#include "pagegenerator.h" +#include "generator.h" QT_BEGIN_NAMESPACE typedef QMap GuidMap; typedef QMap GuidMaps; -class DitaXmlGenerator : public PageGenerator +class DitaXmlGenerator : public Generator { public: enum SinceType { @@ -381,7 +381,6 @@ private: const InnerNode* relative, CodeMarker* marker); void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); 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 -#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES -#include -#endif -#include +#include +#include #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::generators; -QMap > Generator::fmtLeftMaps; -QMap > Generator::fmtRightMaps; -QMap Generator::imgFileExts; -QSet Generator::outputFormats; -QStringList Generator::imageFiles; -QStringList Generator::imageDirs; +QString Generator::baseDir_; QStringList Generator::exampleDirs; QStringList Generator::exampleImgExts; -QStringList Generator::scriptFiles; -QStringList Generator::scriptDirs; -QStringList Generator::styleFiles; -QStringList Generator::styleDirs; +QMap > Generator::fmtLeftMaps; +QMap > Generator::fmtRightMaps; +QList Generator::generators; +QStringList Generator::imageDirs; +QStringList Generator::imageFiles; +QMap Generator::imgFileExts; QString Generator::outDir_; -QString Generator::baseDir_; -QString Generator::project; +QSet Generator::outputFormats; QHash 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("&"), - lt("<"), gt(">"), + lt("<"), quot("""), tag("]*>") { @@ -122,12 +112,1241 @@ 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::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::appendFullNames(Text& text, + const NodeList& nodes, + const Node* relative, + CodeMarker* marker) +{ + NodeList::ConstIterator n = nodes.begin(); + int index = 0; + while (n != nodes.end()) { + appendFullName(text,*n,relative,marker); + text << comma(index++,nodes.count()); + ++n; + } +} + +void Generator::appendSortedNames(Text& text, + const ClassNode *classe, + const QList &classes, + CodeMarker *marker) +{ + QList::ConstIterator r; + QMap 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()); + } +} + +void Generator::appendSortedQmlNames(Text& text, + const Node* base, + const NodeList& subs, + CodeMarker *marker) +{ + QMap 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; + } + } + + QStringList names = classMap.keys(); + names.sort(); + + foreach (const QString &name, names) { + text << classMap[name]; + text << separator(index++, names.count()); + } +} + +/*! + 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(node)->setOutputFileName(fileName); +} + +/*! + 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 */) { } -void Generator::terminateGenerator() +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"))); + } + 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(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); + } + + // 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; +} + +/*! + 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(); + + 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(*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)); + } + + // Find child nodes with since commands. + if ((*child)->isInnerNode()) { + findAllSince(static_cast(*child)); + } + } + ++child; + } +} + +QMap& Generator::formattingLeftMap() +{ + return fmtLeftMaps[format()]; +} + +QMap& Generator::formattingRightMap() +{ + return fmtRightMaps[format()]; +} + +QString Generator::fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const +{ + if (node->type() == Node::Fake) { + const FakeNode* fn = static_cast(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(node))->serviceName().isEmpty()) + return (static_cast(node))->serviceName(); + else + return marker->plainFullName(node, relative); +} + +void Generator::generateAlsoList(const Node *node, CodeMarker *marker) +{ + QList 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 */, + const Node * /* relative */, + CodeMarker * /* marker */) +{ + return 0; +} + +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 %2") + .arg(format()).arg(outFileName())); + 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::generateBody(const Node *node, CodeMarker *marker) +{ + bool quiet = false; + + if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast(node); + if (fake->subType() == Node::Example) { + generateExampleFiles(fake, marker); + } + else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image)) { + quiet = true; + } + } + if (node->doc().isEmpty()) { + if (!quiet && !node->isReimp()) { // ### might be unnecessary + node->location().warning(tr("No documentation for '%1'") + .arg(marker->plainFullName(node))); + } + } + else { + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast(node); + if (func->reimplementedFrom() != 0) + generateReimplementedFrom(func, marker); + } + + if (!generateText(node->doc().body(), node, marker)) { + if (node->isReimp()) + return; + } + + if (node->type() == Node::Enum) { + const EnumNode *enume = (const EnumNode *) node; + + QSet definedItems; + QList::ConstIterator it = enume->items().begin(); + while (it != enume->items().end()) { + definedItems.insert((*it).name()); + ++it; + } + + QSet documentedItems = enume->doc().enumItemNames().toSet(); + QSet allItems = definedItems + documentedItems; + if (allItems.count() > definedItems.count() || + allItems.count() > documentedItems.count()) { + QSet::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(node); + QSet definedParams; + QList::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 documentedParams = func->doc().parameterNames(); + QSet allParams = definedParams + documentedParams; + if (allParams.count() > definedParams.count() + || allParams.count() > documentedParams.count()) { + QSet::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 ¶m, + primaryFunc->parameters()) { + if (param.name() == *a) { + needWarning = false; + break; + } + } + } + } + if (needWarning && !func->isReimp()) + 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 (node->type() == Node::Fake) { + const FakeNode *fake = static_cast(node); + if (fake->subType() == Node::File) { + Text text; + Quoter quoter; + Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); + QString code = quoter.quoteTo(fake->location(), "", ""); + CodeMarker *codeMarker = CodeMarker::markerForFileName(fake->name()); + text << Atom(codeMarker->atomType(), code); + generateText(text, fake, codeMarker); + } + } +} + +void Generator::generateClassLikeNode(const InnerNode * /* classe */, + CodeMarker * /* marker */) +{ +} + +void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) +{ + if (fake->childNodes().isEmpty()) + return; + generateFileList(fake, marker, Node::File, QString("Files:")); + generateFileList(fake, marker, Node::Image, QString("Images:")); +} + +void Generator::generateFakeNode(const FakeNode * /* fake */, + CodeMarker * /* marker */) +{ +} + +/*! + This function is called when the documentation for an + example is being formatted. It outputs the list of source + files comprising the example, and the list of images used + by the example. The images are copied into a subtree of + \c{...doc/html/images/used-in-examples/...} + */ +void Generator::generateFileList(const FakeNode* fake, + CodeMarker* marker, + Node::SubType subtype, + const QString& tag) +{ + int count = 0; + Text text; + OpenedList openedList(OpenedList::Bullet); + + text << Atom::ParaLeft << tag << Atom::ParaRight + << Atom(Atom::ListLeft, openedList.styleString()); + + foreach (const Node* child, fake->childNodes()) { + if (child->subType() == subtype) { + ++count; + QString file = child->name(); + if (subtype == Node::Image) { + if (!file.isEmpty()) { + QDir dirInfo; + QString userFriendlyFilePath; + QString srcPath = Config::findFile(fake->location(), + QStringList(), + exampleDirs, + file, + exampleImgExts, + userFriendlyFilePath); + userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/')); + + QString imgOutDir = outDir_ + "/images/used-in-examples/" + userFriendlyFilePath; + if (!dirInfo.mkpath(imgOutDir)) + fake->location().fatal(tr("Cannot create output directory '%1'") + .arg(imgOutDir)); + + QString imgOutName = Config::copyFile(fake->location(), + srcPath, + file, + imgOutDir); + } + + } + + openedList.next(); + text << Atom(Atom::ListItemNumber, openedList.numberString()) + << Atom(Atom::ListItemLeft, openedList.styleString()) + << Atom::ParaLeft + << Atom(Atom::Link, file) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << file + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom::ParaRight + << Atom(Atom::ListItemRight, openedList.styleString()); + } + } + text << Atom(Atom::ListRight, openedList.styleString()); + if (count > 0) + generateText(text, fake, marker); +} + +void Generator::generateInheritedBy(const ClassNode *classe, + 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); + } +} + +void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) +{ + QList::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); + } +} + +/*! + Recursive writing of HTML files from the root \a node. + + \note NameCollisionNodes are skipped here and processed + later. See HtmlGenerator::generateDisambiguationPages() + for more on this. + */ +void Generator::generateInnerNode(const InnerNode* node) +{ + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast(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())); + } + } + + /* + 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(node); + collisionNodes.append(const_cast(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(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; + } +} + +/*! + 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); + } +} + +#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) +{ + 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 + +#ifdef QDOC_QML +/*! + */ +void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) +{ + // stub. +} +#endif + +#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 != 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 + +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 "; + QString fullName = from->parent()->name() + "::" + from->name() + "()"; + appendFullName(text, from->parent(), fullName, from); + text << "." << Atom::ParaRight; + generateText(text, func, marker); + } + } +} + +void Generator::generateSince(const Node *node, CodeMarker *marker) +{ + 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 "; + + QStringList since = node->since().split(QLatin1Char(' ')); + if (since.count() == 1) { + // Handle legacy use of \since . + if (project.isEmpty()) + text << "version"; + else + text << project; + text << " " << since[0]; + } else { + // Reconstruct the string. + text << " " << since.join(" "); + } + + text << "." << Atom::ParaRight; + generateText(text, node, marker); + } +} + +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); +} + +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; + Node::ThreadSafeness threadSafeness = node->threadSafeness(); + + Text rlink; + rlink << Atom(Atom::Link,"reentrant") + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << "reentrant" + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + Text tlink; + tlink << Atom(Atom::Link,"thread-safe") + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << "thread-safe" + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + switch (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 " + << rlink + << "." + << Atom::ParaRight; + break; + case Node::Reentrant: + case Node::ThreadSafe: + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Note:" + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) + << " "; + + if (node->isInnerNode()) { + const InnerNode* innerNode = static_cast(node); + text << "All functions in this " + << typeString(node) + << " are "; + if (threadSafeness == Node::ThreadSafe) + text << tlink; + else + text << rlink; + + bool exceptions = false; + NodeList reentrant; + NodeList threadsafe; + NodeList nonreentrant; + NodeList::ConstIterator c = innerNode->childNodes().begin(); + while (c != innerNode->childNodes().end()) { + + if ((*c)->status() != Node::Obsolete){ + switch ((*c)->threadSafeness()) { + case Node::Reentrant: + reentrant.append(*c); + if (threadSafeness == Node::ThreadSafe) + exceptions = true; + break; + case Node::ThreadSafe: + threadsafe.append(*c); + if (threadSafeness == Node::Reentrant) + exceptions = true; + break; + case Node::NonReentrant: + nonreentrant.append(*c); + exceptions = true; + break; + default: + break; + } + } + ++c; + } + if (!exceptions) + text << "."; + else if (threadSafeness == Node::Reentrant) { + if (nonreentrant.isEmpty()) { + if (!threadsafe.isEmpty()) { + text << ", but "; + appendFullNames(text,threadsafe,innerNode,marker); + singularPlural(text,threadsafe); + text << " also " << tlink << "."; + } + else + text << "."; + } + else { + text << ", except for "; + appendFullNames(text,nonreentrant,innerNode,marker); + text << ", which"; + singularPlural(text,nonreentrant); + text << " nonreentrant."; + if (!threadsafe.isEmpty()) { + text << " "; + appendFullNames(text,threadsafe,innerNode,marker); + singularPlural(text,threadsafe); + text << " " << tlink << "."; + } + } + } + else { // thread-safe + if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) { + text << ", except for "; + if (!reentrant.isEmpty()) { + appendFullNames(text,reentrant,innerNode,marker); + text << ", which"; + singularPlural(text,reentrant); + text << " only " << rlink; + if (!nonreentrant.isEmpty()) + text << ", and "; + } + if (!nonreentrant.isEmpty()) { + appendFullNames(text,nonreentrant,innerNode,marker); + text << ", which"; + singularPlural(text,nonreentrant); + text << " nonreentrant."; + } + text << "."; + } + } + } + else { + text << "This " << typeString(node) << " is "; + if (threadSafeness == Node::ThreadSafe) + text << tlink; + else + text << rlink; + text << "."; + } + text << Atom::ParaRight; + } + generateText(text,node,marker); +} + +/*! + This function is recursive. + */ +void Generator::generateTree(const Tree *tree) +{ + generateInnerNode(tree->root()); +} + +Generator *Generator::generatorForFormat(const QString& format) +{ + QList::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if ((*g)->format() == format) + return *g; + ++g; + } + return 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. + + \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(inner->doc().metaTagMap()); + QStringMultiMap::iterator i = metaTagMap.find(t); + if (i != metaTagMap.end()) { + s = i.value(); + metaTagMap.erase(i); + } + 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. + + \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) +{ + QStringList s; + QStringMultiMap& metaTagMap = const_cast(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; } void Generator::initialize(const Config &config) @@ -311,460 +1530,126 @@ void Generator::initialize(const Config &config) outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); } -void Generator::terminate() -{ - QList::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(); -} - -Generator *Generator::generatorForFormat(const QString& format) -{ - QList::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::initializeGenerator(const Config & /* config */) { } -void Generator::endText(const Node * /* relative */, - CodeMarker * /* marker */) +bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) { -} - -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 */) -{ -} - -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; -} - -#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 != 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 - -void Generator::generateBody(const Node *node, CodeMarker *marker) -{ - bool quiet = false; - - if (node->type() == Node::Fake) { - const FakeNode *fake = static_cast(node); - if (fake->subType() == Node::Example) { - generateExampleFiles(fake, marker); - } - else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image)) { - quiet = true; - } - } - if (node->doc().isEmpty()) { - if (!quiet && !node->isReimp()) { // ### might be unnecessary - node->location().warning(tr("No documentation for '%1'") - .arg(marker->plainFullName(node))); - } - } - else { - if (node->type() == Node::Function) { - const FunctionNode *func = static_cast(node); - if (func->reimplementedFrom() != 0) - generateReimplementedFrom(func, marker); - } - - if (!generateText(node->doc().body(), node, marker)) { - if (node->isReimp()) - return; - } - - if (node->type() == Node::Enum) { - const EnumNode *enume = (const EnumNode *) node; - - QSet definedItems; - QList::ConstIterator it = enume->items().begin(); - while (it != enume->items().end()) { - definedItems.insert((*it).name()); - ++it; - } - - QSet documentedItems = enume->doc().enumItemNames().toSet(); - QSet allItems = definedItems + documentedItems; - if (allItems.count() > definedItems.count() || - allItems.count() > documentedItems.count()) { - QSet::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(node); - QSet definedParams; - QList::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 documentedParams = func->doc().parameterNames(); - QSet allParams = definedParams + documentedParams; - if (allParams.count() > definedParams.count() - || allParams.count() > documentedParams.count()) { - QSet::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 ¶m, - primaryFunc->parameters()) { - if (param.name() == *a) { - needWarning = false; - break; - } - } - } - } - if (needWarning && !func->isReimp()) - 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 (node->type() == Node::Fake) { - const FakeNode *fake = static_cast(node); - if (fake->subType() == Node::File) { - Text text; - Quoter quoter; - Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); - QString code = quoter.quoteTo(fake->location(), "", ""); - CodeMarker *codeMarker = CodeMarker::markerForFileName(fake->name()); - text << Atom(codeMarker->atomType(), code); - generateText(text, fake, codeMarker); - } - } -} - -void Generator::generateAlsoList(const Node *node, CodeMarker *marker) -{ - QList 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); - } + return atom->next() != 0 && atom->next()->type() == expectedAtomType; } /*! - Generate a list of maintainers in the output + 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. */ -void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker) +QTextStream &Generator::out() { - QStringList sl = getMetadataElements(node,"maintainer"); + return *outStreamStack.top(); +} - if (!sl.isEmpty()) { - Text text; - text << Atom::ParaLeft - << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) - << "Maintained by: " - << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); +QString Generator::outputPrefix(const QString &nodeType) +{ + return outputPrefixes[nodeType]; +} - for (int i = 0; i < sl.size(); ++i) - text << sl.at(i) << separator(i, sl.size()); +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; - text << Atom::ParaRight; - generateText(text, node, marker); + +#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; } -} -void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) -{ - QList::ConstIterator r; - int index; + if (debug) + qDebug() << "haystack:" << src << "needle:" << tag << "i:" <baseClasses().isEmpty()) { - Text text; - text << Atom::ParaLeft - << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) - << "Inherits: " - << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); + // skip tag + i += tag.length(); - 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* ) -{ - // stub. -} -#endif - -void Generator::generateInheritedBy(const ClassNode *classe, - 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); - } -} - -/*! - This function is called when the documentation for an - example is being formatted. It outputs the list of source - files comprising the example, and the list of images used - by the example. The images are copied into a subtree of - \c{...doc/html/images/used-in-examples/...} - */ -void Generator::generateFileList(const FakeNode* fake, - CodeMarker* marker, - Node::SubType subtype, - const QString& tag) -{ - int count = 0; - Text text; - OpenedList openedList(OpenedList::Bullet); - - text << Atom::ParaLeft << tag << Atom::ParaRight - << Atom(Atom::ListLeft, openedList.styleString()); - - foreach (const Node* child, fake->childNodes()) { - if (child->subType() == subtype) { - ++count; - QString file = child->name(); - if (subtype == Node::Image) { - if (!file.isEmpty()) { - QDir dirInfo; - QString userFriendlyFilePath; - QString srcPath = Config::findFile(fake->location(), - QStringList(), - exampleDirs, - file, - exampleImgExts, - userFriendlyFilePath); - userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/')); - - QString imgOutDir = outDir_ + "/images/used-in-examples/" + userFriendlyFilePath; - if (!dirInfo.mkpath(imgOutDir)) - fake->location().fatal(tr("Cannot create output directory '%1'") - .arg(imgOutDir)); - - QString imgOutName = Config::copyFile(fake->location(), - srcPath, - file, - imgOutDir); - } - - } - - openedList.next(); - text << Atom(Atom::ListItemNumber, openedList.numberString()) - << Atom(Atom::ListItemLeft, openedList.styleString()) - << Atom::ParaLeft - << Atom(Atom::Link, file) - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << file - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) - << Atom::ParaRight - << Atom(Atom::ListItemRight, openedList.styleString()); + // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*()"); + 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"; } } - text << Atom(Atom::ListRight, openedList.styleString()); - if (count > 0) - generateText(text, fake, marker); -} + SKIP_SPACE; + SKIP_CHAR('>'); -void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) -{ - if (fake->childNodes().isEmpty()) - return; - generateFileList(fake, marker, Node::File, QString("Files:")); - generateFileList(fake, marker, Node::Image, QString("Images:")); -} - -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++); + // find contents up to closing " + 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; } - return t; + + *contents = QStringRef(&src, j, i - j); + + i += tag.length() + 4; + + *pos = i; + if (debug) + qDebug() << " tag " << tag << " found: pos now: " << i; + return true; +#undef SKIP_CHAR } QString Generator::plainCode(const QString& markedCode) @@ -778,76 +1663,33 @@ QString Generator::plainCode(const QString& markedCode) return t; } -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"; - } - } - case Node::Enum: - return "enum"; - case Node::Typedef: - return "typedef"; - case Node::Function: - return "function"; - case Node::Property: - return "property"; - default: - return "documentation"; - } -} - -/*! - 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; -} - void Generator::setImageFileExtensions(const QStringList& extensions) { imgFileExts[format()] = extensions; } -void Generator::unknownAtom(const Atom *atom) +void Generator::singularPlural(Text& text, const NodeList& nodes) { - Location::internalError(tr("unknown atom type '%1' in %2 generator") - .arg(atom->typeString()).arg(format())); + if (nodes.count() == 1) + text << " is"; + else + text << " are"; } -bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) +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; +} + +void Generator::startText(const Node * /* relative */, + CodeMarker * /* marker */) { - return atom->next() != 0 && atom->next()->type() == expectedAtomType; } void Generator::supplementAlsoList(const Node *node, QList &alsoList) @@ -901,18 +1743,31 @@ void Generator::supplementAlsoList(const Node *node, QList &alsoList) } } -QMap& Generator::formattingLeftMap() +void Generator::terminate() { - return fmtLeftMaps[format()]; + QList::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(); } -QMap& Generator::formattingRightMap() +void Generator::terminateGenerator() { - return fmtRightMaps[format()]; } -/* - Trims trailimng whitespace off the \a string and returns +/*! + Trims trailing whitespace off the \a string and returns the trimmed string. */ QString Generator::trimmedTrailing(const QString& string) @@ -923,573 +1778,43 @@ QString Generator::trimmedTrailing(const QString& string) return trimmed; } -void Generator::generateStatus(const Node *node, CodeMarker *marker) +QString Generator::typeString(const Node *node) { - 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; + 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"; } - break; - case Node::Internal: + } + case Node::Enum: + return "enum"; + case Node::Typedef: + return "typedef"; + case Node::Function: + return "function"; + case Node::Property: + return "property"; default: - break; - } - generateText(text, node, marker); -} - -void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) -{ - Text text; - Text theStockLink; - Node::ThreadSafeness threadSafeness = node->threadSafeness(); - - Text rlink; - rlink << Atom(Atom::Link,"reentrant") - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << "reentrant" - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - - Text tlink; - tlink << Atom(Atom::Link,"thread-safe") - << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << "thread-safe" - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); - - switch (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 " - << rlink - << "." - << Atom::ParaRight; - break; - case Node::Reentrant: - case Node::ThreadSafe: - text << Atom::ParaLeft - << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) - << "Note:" - << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) - << " "; - - if (node->isInnerNode()) { - const InnerNode* innerNode = static_cast(node); - text << "All functions in this " - << typeString(node) - << " are "; - if (threadSafeness == Node::ThreadSafe) - text << tlink; - else - text << rlink; - - bool exceptions = false; - NodeList reentrant; - NodeList threadsafe; - NodeList nonreentrant; - NodeList::ConstIterator c = innerNode->childNodes().begin(); - while (c != innerNode->childNodes().end()) { - - if ((*c)->status() != Node::Obsolete){ - switch ((*c)->threadSafeness()) { - case Node::Reentrant: - reentrant.append(*c); - if (threadSafeness == Node::ThreadSafe) - exceptions = true; - break; - case Node::ThreadSafe: - threadsafe.append(*c); - if (threadSafeness == Node::Reentrant) - exceptions = true; - break; - case Node::NonReentrant: - nonreentrant.append(*c); - exceptions = true; - break; - default: - break; - } - } - ++c; - } - if (!exceptions) - text << "."; - else if (threadSafeness == Node::Reentrant) { - if (nonreentrant.isEmpty()) { - if (!threadsafe.isEmpty()) { - text << ", but "; - appendFullNames(text,threadsafe,innerNode,marker); - singularPlural(text,threadsafe); - text << " also " << tlink << "."; - } - else - text << "."; - } - else { - text << ", except for "; - appendFullNames(text,nonreentrant,innerNode,marker); - text << ", which"; - singularPlural(text,nonreentrant); - text << " nonreentrant."; - if (!threadsafe.isEmpty()) { - text << " "; - appendFullNames(text,threadsafe,innerNode,marker); - singularPlural(text,threadsafe); - text << " " << tlink << "."; - } - } - } - else { // thread-safe - if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) { - text << ", except for "; - if (!reentrant.isEmpty()) { - appendFullNames(text,reentrant,innerNode,marker); - text << ", which"; - singularPlural(text,reentrant); - text << " only " << rlink; - if (!nonreentrant.isEmpty()) - text << ", and "; - } - if (!nonreentrant.isEmpty()) { - appendFullNames(text,nonreentrant,innerNode,marker); - text << ", which"; - singularPlural(text,nonreentrant); - text << " nonreentrant."; - } - text << "."; - } - } - } - else { - text << "This " << typeString(node) << " is "; - if (threadSafeness == Node::ThreadSafe) - text << tlink; - else - text << rlink; - text << "."; - } - 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); - if (node->type() == Node::Enum) - text << " was introduced or modified in "; - else - text << " was introduced in "; - - QStringList since = node->since().split(QLatin1Char(' ')); - if (since.count() == 1) { - // Handle legacy use of \since . - if (project.isEmpty()) - text << "version"; - else - text << project; - text << " " << since[0]; - } else { - // Reconstruct the string. - text << " " << since.join(" "); - } - - text << "." << Atom::ParaRight; - generateText(text, node, marker); + return "documentation"; } } -void Generator::generateReimplementedFrom(const FunctionNode *func, - CodeMarker *marker) +void Generator::unknownAtom(const Atom *atom) { - 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); - } - } -} - -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 %2") - .arg(format()).arg(outFileName())); - 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::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::appendFullNames(Text& text, - const NodeList& nodes, - const Node* relative, - CodeMarker* marker) -{ - NodeList::ConstIterator n = nodes.begin(); - int index = 0; - while (n != nodes.end()) { - appendFullName(text,*n,relative,marker); - text << comma(index++,nodes.count()); - ++n; - } -} - -void Generator::appendSortedNames(Text& text, - const ClassNode *classe, - const QList &classes, - CodeMarker *marker) -{ - QList::ConstIterator r; - QMap 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()); - } -} - -void Generator::appendSortedQmlNames(Text& text, - const Node* base, - const NodeList& subs, - CodeMarker *marker) -{ - QMap 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; - } - } - - QStringList names = classMap.keys(); - names.sort(); - - foreach (const QString &name, names) { - text << classMap[name]; - text << separator(index++, names.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) { - const FakeNode* fn = static_cast(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(node))->serviceName().isEmpty()) - return (static_cast(node))->serviceName(); - else - return marker->plainFullName(node, relative); -} - -QString Generator::outputPrefix(const QString &nodeType) -{ - return outputPrefixes[nodeType]; -} - -/*! - 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(inner->doc().metaTagMap()); - QStringMultiMap::iterator i = metaTagMap.find(t); - if (i != metaTagMap.end()) { - s = i.value(); - metaTagMap.erase(i); - } - 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. - - \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) -{ - QStringList s; - QStringMultiMap& metaTagMap = const_cast(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. - */ -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(*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)); - } - - // Find child nodes with since commands. - if ((*child)->isInnerNode()) { - findAllSince(static_cast(*child)); - } - } - ++child; - } + Location::internalError(tr("unknown atom type '%1' in %2 generator") + .arg(atom->typeString()).arg(format())); } QT_END_NAMESPACE diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index 4021a85f61..bc1b12b958 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -46,22 +46,23 @@ #ifndef GENERATOR_H #define GENERATOR_H -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "node.h" #include "text.h" QT_BEGIN_NAMESPACE +typedef QMap NewClassMaps; +typedef QMap NewSinceMaps; typedef QMap NodeMap; typedef QMultiMap NodeMultiMap; -typedef QMap NewSinceMaps; typedef QMap ParentMaps; -typedef QMap NewClassMaps; class ClassNode; class Config; @@ -80,87 +81,124 @@ public: Generator(); virtual ~Generator(); + virtual bool canHandleFormat(const QString &format) { return format == this->format(); } + virtual QString format() = 0; + virtual void generateTree(const Tree *tree); virtual void initializeGenerator(const Config &config); virtual void terminateGenerator(); - virtual QString format() = 0; - virtual bool canHandleFormat(const QString &format) { return format == this->format(); } - virtual void generateTree(const Tree *tree) = 0; - static void initialize(const Config& config); - static void terminate(); - static Generator *generatorForFormat(const QString& format); - static const QString& outputDir() { return outDir_; } static const QString& baseDir() { return baseDir_; } + static Generator *generatorForFormat(const QString& format); + static void initialize(const Config& config); + static const QString& outputDir() { return outDir_; } + static void terminate(); protected: - virtual void startText(const Node *relative, CodeMarker *marker); + virtual void beginSubPage(const InnerNode* node, const QString& fileName); + virtual void endSubPage(); virtual void endText(const Node *relative, CodeMarker *marker); + virtual QString fileBase(const Node* node) const; + virtual QString fileExtension(const Node* node) const = 0; + virtual QString fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const; + virtual void generateAlsoList(const Node *node, CodeMarker *marker); virtual int generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker); + virtual void generateBody(const Node *node, CodeMarker *marker); virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker); virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker); - - virtual bool generateText(const Text& text, - const Node *relative, - CodeMarker *marker); + virtual void generateInheritedBy(const ClassNode *classe, + CodeMarker *marker); + virtual void generateInherits(const ClassNode *classe, + CodeMarker *marker); + virtual void generateInnerNode(const InnerNode *node); + virtual void generateMaintainerList(const InnerNode* node, CodeMarker* marker); + virtual void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); + virtual void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); virtual bool generateQmlText(const Text& text, const Node *relative, CodeMarker *marker, const QString& qmlName); - virtual void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - virtual void generateBody(const Node *node, CodeMarker *marker); - virtual void generateAlsoList(const Node *node, CodeMarker *marker); - virtual void generateMaintainerList(const InnerNode* node, CodeMarker* marker); - virtual void generateInherits(const ClassNode *classe, - CodeMarker *marker); - virtual void generateInheritedBy(const ClassNode *classe, - CodeMarker *marker); + virtual bool generateText(const Text& text, + const Node *relative, + CodeMarker *marker); + virtual QString imageFileName(const Node *relative, const QString& fileBase); + virtual QString outFileName() { return QString(); } + virtual int skipAtoms(const Atom *atom, Atom::Type type) const; + virtual void startText(const Node *relative, CodeMarker *marker); + virtual QString typeString(const Node *node); - void generateThreadSafeness(const Node *node, CodeMarker *marker); - void generateSince(const Node *node, CodeMarker *marker); - void generateStatus(const Node *node, CodeMarker *marker); + static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType); + static QString outputPrefix(const QString &nodeType); + static void singularPlural(Text& text, const NodeList& nodes); + static void supplementAlsoList(const Node *node, QList &alsoList); + static QString trimmedTrailing(const QString &string); + static QString sinceTitles[]; + + QString fileName(const Node* node) const; + void findAllSince(const InnerNode *node); + QMap &formattingLeftMap(); + QMap &formattingRightMap(); const Atom* generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker, bool generate, int& numGeneratedAtoms); + void generateExampleFiles(const FakeNode *fake, CodeMarker *marker); void generateFileList(const FakeNode* fake, CodeMarker* marker, Node::SubType subtype, const QString& tag); - void generateExampleFiles(const FakeNode *fake, CodeMarker *marker); - - virtual int skipAtoms(const Atom *atom, Atom::Type type) const; - virtual QString fullName(const Node *node, - const Node *relative, - CodeMarker *marker) const; - - virtual QString outFileName() { return QString(); } - - QString indent(int level, const QString& markedCode); - QString plainCode(const QString& markedCode); - virtual QString typeString(const Node *node); - virtual QString imageFileName(const Node *relative, const QString& fileBase); - void setImageFileExtensions(const QStringList& extensions); - void unknownAtom(const Atom *atom); - QMap &formattingLeftMap(); - QMap &formattingRightMap(); - QMap editionModuleMap; - QMap editionGroupMap; - - static QString trimmedTrailing(const QString &string); - static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType); - static void supplementAlsoList(const Node *node, QList &alsoList); - static QString outputPrefix(const QString &nodeType); - + void generateSince(const Node *node, CodeMarker *marker); + void generateStatus(const Node *node, CodeMarker *marker); + void generateThreadSafeness(const Node *node, CodeMarker *marker); QString getMetadataElement(const InnerNode* inner, const QString& t); QStringList getMetadataElements(const InnerNode* inner, const QString& t); - void findAllSince(const InnerNode *node); + QString indent(int level, const QString& markedCode); + QTextStream& out(); + bool parseArg(const QString& src, + const QString& tag, + int* pos, + int n, + QStringRef* contents, + QStringRef* par1 = 0, + bool debug = false); + QString plainCode(const QString& markedCode); + void setImageFileExtensions(const QStringList& extensions); + void unknownAtom(const Atom *atom); + + QList collisionNodes; + QMap editionGroupMap; + QMap editionModuleMap; + QString naturalLanguage; + QTextCodec* outputCodec; + QString outputEncoding; + QStack outStreamStack; + NewClassMaps newClassMaps; + NewClassMaps newQmlClassMaps; + NewSinceMaps newSinceMaps; private: - void generateReimplementedFrom(const FunctionNode *func, - CodeMarker *marker); + static QString baseDir_; + static QStringList exampleDirs; + static QStringList exampleImgExts; + static QMap > fmtLeftMaps; + static QMap > fmtRightMaps; + static QList generators; + static QStringList imageDirs; + static QStringList imageFiles; + static QMap imgFileExts; + static QString project; + static QString outDir_; + static QSet outputFormats; + static QHash outputPrefixes; + static QStringList scriptDirs; + static QStringList scriptFiles; + static QStringList styleDirs; + static QStringList styleFiles; + void appendFullName(Text& text, const Node *apparentNode, const Node *relative, @@ -178,42 +216,18 @@ private: const ClassNode *classe, const QList &classes, CodeMarker *marker); - -protected: void appendSortedQmlNames(Text& text, const Node* base, const NodeList& subs, CodeMarker *marker); + void generateReimplementedFrom(const FunctionNode *func, + CodeMarker *marker); - static QString sinceTitles[]; - NewSinceMaps newSinceMaps; - NewClassMaps newClassMaps; - NewClassMaps newQmlClassMaps; - -private: QString amp; - QString lt; QString gt; + QString lt; QString quot; QRegExp tag; - - static QList generators; - static QMap > fmtLeftMaps; - static QMap > fmtRightMaps; - static QMap imgFileExts; - static QSet outputFormats; - static QStringList imageFiles; - static QStringList imageDirs; - static QStringList exampleDirs; - static QStringList exampleImgExts; - static QStringList scriptFiles; - static QStringList scriptDirs; - static QStringList styleFiles; - static QStringList styleDirs; - static QString outDir_; - static QString baseDir_; - static QString project; - static QHash outputPrefixes; }; QT_END_NAMESPACE diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 9b886564c4..0662303d1e 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -273,7 +273,7 @@ void HtmlGenerator::generateTree(const Tree *tree) findAllNamespaces(tree->root()); findAllSince(tree->root()); - PageGenerator::generateTree(tree); + Generator::generateTree(tree); reportOrphans(tree->root()); generateDisambiguationPages(); @@ -1375,7 +1375,7 @@ void HtmlGenerator::generateDisambiguationPages() for (int i=0; iclearCurrentChild(); - beginSubPage(ncn, PageGenerator::fileName(ncn)); + beginSubPage(ncn, Generator::fileName(ncn)); QString fullTitle = "Name Collision: " + ncn->fullTitle(); QString htmlTitle = fullTitle; CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath()); @@ -3270,7 +3270,7 @@ QString HtmlGenerator::fileBase(const Node *node) const { QString result; - result = PageGenerator::fileBase(node); + result = Generator::fileBase(node); if (!node->isInnerNode()) { switch (node->status()) { @@ -3295,7 +3295,7 @@ QString HtmlGenerator::fileName(const Node *node) if (static_cast(node)->subType() == Node::Image) return node->name(); } - return PageGenerator::fileName(node); + return Generator::fileName(node); } QString HtmlGenerator::refForNode(const Node *node) @@ -3719,7 +3719,7 @@ const QPair HtmlGenerator::anchorForNode(const Node *node) { QPair anchorPair; - anchorPair.first = PageGenerator::fileName(node); + anchorPair.first = Generator::fileName(node); if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast(node); anchorPair.second = fakeNode->title(); @@ -4227,25 +4227,6 @@ void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* mar } } -/*! - Output the "Inherit by" list for the QML element, - if it is inherited by any other elements. - */ -void HtmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker) -{ - 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); - } - } -} - /*! Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" line for the QML element, if there should be one. @@ -4332,7 +4313,7 @@ bool HtmlGenerator::generatePageElement(QXmlStreamWriter& writer, QString url = node->outputSubdirectory(); if (!url.isEmpty()) url.append(QLatin1Char('/')); - url.append(PageGenerator::fileName(node)); + url.append(Generator::fileName(node)); writer.writeStartElement("page"); @@ -5042,7 +5023,7 @@ QXmlStreamWriter& HtmlGenerator::xmlWriter() */ void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName) { - PageGenerator::beginSubPage(node,fileName); + Generator::beginSubPage(node,fileName); QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); xmlWriterStack.push(writer); writer->setAutoFormatting(true); @@ -5061,7 +5042,7 @@ void HtmlGenerator::endDitamapPage() { xmlWriter().writeEndDocument(); delete xmlWriterStack.pop(); - PageGenerator::endSubPage(); + Generator::endSubPage(); } /*! diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 24c5fb7179..841c226b75 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -46,18 +46,18 @@ #ifndef HTMLGENERATOR_H #define HTMLGENERATOR_H -#include -#include +#include +#include #include #include "codemarker.h" #include "config.h" -#include "pagegenerator.h" +#include "generator.h" QT_BEGIN_NAMESPACE class HelpProjectWriter; -class HtmlGenerator : public PageGenerator +class HtmlGenerator : public Generator { public: enum SinceType { @@ -180,7 +180,6 @@ private: const InnerNode *relative, CodeMarker *marker); void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); - void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); diff --git a/src/tools/qdoc/pagegenerator.cpp b/src/tools/qdoc/pagegenerator.cpp deleted file mode 100644 index 6a52b7755a..0000000000 --- a/src/tools/qdoc/pagegenerator.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* - pagegenerator.cpp -*/ - -#include -#include -#include -#include "codemarker.h" -#include "pagegenerator.h" -#include "tree.h" - -QT_BEGIN_NAMESPACE - -/*! - Nothing to do in the constructor. - */ -PageGenerator::PageGenerator() - : outputCodec(0) -{ - // nothing. -} - -/*! - The destructor - */ -PageGenerator::~PageGenerator() -{ - while (!outStreamStack.isEmpty()) - endSubPage(); -} - -bool PageGenerator::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:" <).*()"); - 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"; - } - } - SKIP_SPACE; - SKIP_CHAR('>'); - - // find contents up to closing " - 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; - } - - *contents = QStringRef(&src, j, i - j); - - i += tag.length() + 4; - - *pos = i; - if (debug) - qDebug() << " tag " << tag << " found: pos now: " << i; - return true; -#undef SKIP_CHAR -} - -/*! - This function is recursive. - */ -void PageGenerator::generateTree(const Tree *tree) -{ - generateInnerNode(tree->root()); -} - -QString PageGenerator::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"))); - } - 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(node); - if (ncn->currentChild()) - return fileBase(ncn->currentChild()); - base.prepend("collision-"); - } -#ifdef QDOC2_COMPAT - if (base.endsWith(".html")) - base.truncate(base.length() - 5); -#endif - } - - // 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; -} - -/*! - 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 PageGenerator::fileName(const Node* node) const -{ - if (!node->url().isEmpty()) - return node->url(); - - QString name = fileBase(node); - name += QLatin1Char('.'); - name += fileExtension(node); - return name; -} - -/*! - Return the current output file name. - */ -QString PageGenerator::outFileName() -{ - return QFileInfo(static_cast(out().device())->fileName()).fileName(); -} - -/*! - 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 PageGenerator::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(node)->setOutputFileName(fileName); -} - -/*! - 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 PageGenerator::endSubPage() -{ - outStreamStack.top()->flush(); - delete outStreamStack.top()->device(); - delete outStreamStack.pop(); -} - -/*! - Used for writing to the current output stream. Returns a - reference to the crrent output stream, which is then used - with the \c {<<} operator for writing. - */ -QTextStream &PageGenerator::out() -{ - return *outStreamStack.top(); -} - -/*! - Recursive writing of HTML files from the root \a node. - - \note NameCollisionNodes are skipped here and processed - later. See HtmlGenerator::generateDisambiguationPages() - for more on this. - */ -void -PageGenerator::generateInnerNode(const InnerNode* node) -{ - if (!node->url().isNull()) - return; - - if (node->type() == Node::Fake) { - const FakeNode *fakeNode = static_cast(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())); - } - } - - /* - 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(node); - collisionNodes.append(const_cast(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(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; - } -} - -QT_END_NAMESPACE diff --git a/src/tools/qdoc/pagegenerator.h b/src/tools/qdoc/pagegenerator.h deleted file mode 100644 index da0d32d2ff..0000000000 --- a/src/tools/qdoc/pagegenerator.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* - pagegenerator.h -*/ - -#ifndef PAGEGENERATOR_H -#define PAGEGENERATOR_H - -#include -#include -#include "generator.h" -#include "location.h" - -QT_BEGIN_NAMESPACE - -class QTextCodec; -class ClassNode; -class InnerNode; -class NamespaceNode; -class NameCollisionNode; - -class PageGenerator : public Generator -{ -public: - PageGenerator(); - ~PageGenerator(); - - virtual void generateTree(const Tree *tree); - virtual void generateDisambiguationPages() { } - -protected: - virtual QString fileBase(const Node* node) const; - virtual QString fileExtension(const Node* node) const = 0; - QString fileName(const Node* node) const; - QString outFileName(); - virtual void beginSubPage(const InnerNode* node, const QString& fileName); - virtual void endSubPage(); - virtual void generateInnerNode(const InnerNode *node); - QTextStream& out(); - - QString naturalLanguage; - QString outputEncoding; - QTextCodec* outputCodec; - bool parseArg(const QString& src, - const QString& tag, - int* pos, - int n, - QStringRef* contents, - QStringRef* par1 = 0, - bool debug = false); - -protected: - QStack outStreamStack; - QList collisionNodes; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/tools/qdoc/qdoc.pro b/src/tools/qdoc/qdoc.pro index 03de9f91df..09f9b7a0c1 100644 --- a/src/tools/qdoc/qdoc.pro +++ b/src/tools/qdoc/qdoc.pro @@ -39,7 +39,6 @@ HEADERS += atom.h \ location.h \ node.h \ openedlist.h \ - pagegenerator.h \ plaincodemarker.h \ puredocparser.h \ quoter.h \ @@ -65,7 +64,6 @@ SOURCES += atom.cpp \ main.cpp \ node.cpp \ openedlist.cpp \ - pagegenerator.cpp \ plaincodemarker.cpp \ puredocparser.cpp \ quoter.cpp \