diff options
Diffstat (limited to 'src/qdoc/node.cpp')
-rw-r--r-- | src/qdoc/node.cpp | 3038 |
1 files changed, 3038 insertions, 0 deletions
diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp new file mode 100644 index 000000000..c1cf076f4 --- /dev/null +++ b/src/qdoc/node.cpp @@ -0,0 +1,3038 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "node.h" +#include "tree.h" +#include "codemarker.h" +#include "cppcodeparser.h" +#include <quuid.h> +#include "qdocdatabase.h" +#include <qdebug.h> +#include "generator.h" +#include "tokenizer.h" + +QT_BEGIN_NAMESPACE + +int Node::propertyGroupCount_ = 0; +QStringMap Node::operators_; +QMap<QString,Node::NodeType> Node::goals_; + +/*! + Initialize the map of search goals. This is called once + by QDocDatabase::initializeDB(). The map key is a string + representing a value in the enum Node::NodeType. The map value + is the enum value. + + There should be an entry in the map for each value in the + NodeType enum. + */ +void Node::initialize() +{ + goals_.insert("class", Node::Class); + goals_.insert("qmltype", Node::QmlType); + goals_.insert("page", Node::Document); + goals_.insert("function", Node::Function); + goals_.insert("property", Node::Property); + goals_.insert("variable", Node::Variable); + goals_.insert("group", Node::Group); + goals_.insert("module", Node::Module); + goals_.insert("qmlmodule", Node::QmlModule); + goals_.insert("qmppropertygroup", Node::QmlPropertyGroup); + goals_.insert("qmlproperty", Node::QmlProperty); + goals_.insert("qmlsignal", Node::QmlSignal); + goals_.insert("qmlsignalhandler", Node::QmlSignalHandler); + goals_.insert("qmlmethod", Node::QmlMethod); + goals_.insert("qmlbasictype", Node::QmlBasicType); + goals_.insert("enum", Node::Enum); + goals_.insert("typedef", Node::Typedef); + goals_.insert("namespace", Node::Namespace); +} + +/*! + Increment the number of property groups seen in the current + file, and return the new value. + */ +int Node::incPropertyGroupCount() { return ++propertyGroupCount_; } + +/*! + Reset the number of property groups seen in the current file + to 0, because we are starting a new file. + */ +void Node::clearPropertyGroupCount() { propertyGroupCount_ = 0; } + +/*! + \class Node + \brief The Node class is a node in the Tree. + + A Node represents a class or function or something else + from the source code.. + */ + +/*! + When this Node is destroyed, if it has a parent Node, it + removes itself from the parent node's child list. + */ +Node::~Node() +{ + if (parent_) + parent_->removeChild(this); + + if (relatesTo_) + removeRelates(); +} + +/*! + Removes this node from the aggregate's list of related + nodes, or if this node has created a dummy "relates" + aggregate, deletes it. +*/ +void Node::removeRelates() +{ + if (!relatesTo_) + return; + + if (relatesTo_->isDocumentNode() && !relatesTo_->parent()) { + delete relatesTo_; + relatesTo_ = 0; + } else { + relatesTo_->removeRelated(this); + } +} + +/*! + Returns this node's name member. Appends "()" to the returned + name, if this node is a function node. + */ +QString Node::plainName() const +{ + if (type() == Node::Function) + return name_ + QLatin1String("()"); + return name_; +} + +/*! + Constructs and returns the node's fully qualified name by + recursively ascending the parent links and prepending each + parent name + "::". Breaks out when the parent pointer is + \a relative. Almost all calls to this function pass 0 for + \a relative. + */ +QString Node::plainFullName(const Node* relative) const +{ + if (name_.isEmpty()) + return QLatin1String("global"); + + QString fullName; + const Node* node = this; + while (node) { + fullName.prepend(node->plainName()); + if (node->parent() == relative || node->parent()->name().isEmpty()) + break; + fullName.prepend(QLatin1String("::")); + node = node->parent(); + } + return fullName; +} + +/*! + Constructs and returns this node's full name. + */ +QString Node::fullName(const Node* relative) const +{ + if ((isDocumentNode() || isGroup()) && !title().isEmpty()) + return title(); + return plainFullName(relative); +} + +/*! + Try to match this node's type and subtype with one of the + pairs in \a types. If a match is found, return true. If no + match is found, return false. + + \a types is a list of type/subtype pairs, where the first + value in the pair is a Node::NodeType, and the second value is + a Node::DocSubtype. The second value is used in the match if + this node's type is Node::Document. + */ +bool Node::match(const NodeTypeList& types) const +{ + for (int i=0; i<types.size(); ++i) { + if (type() == types.at(i).first) { + if (type() == Node::Document) { + if (docSubtype() == types.at(i).second) + return true; + } + else + return true; + } + } + return false; +} + +/*! + Sets this Node's Doc to \a doc. If \a replace is false and + this Node already has a Doc, a warning is reported that the + Doc is being overridden, and it reports where the previous + Doc was found. If \a replace is true, the Doc is replaced + silently. + */ +void Node::setDoc(const Doc& doc, bool replace) +{ + if (!doc_.isEmpty() && !replace) { + doc.location().warning(tr("Overrides a previous doc")); + doc_.location().warning(tr("(The previous doc is here)")); + } + doc_ = doc; +} + +/*! + Construct a node with the given \a type and having the + given \a parent and \a name. The new node is added to the + parent's child list. + */ +Node::Node(NodeType type, Aggregate *parent, const QString& name) + : nodeType_((unsigned char) type), + access_((unsigned char) Public), + safeness_((unsigned char) UnspecifiedSafeness), + pageType_((unsigned char) NoPageType), + status_((unsigned char) Active), + indexNodeFlag_(false), + parent_(parent), + relatesTo_(0), + name_(name) +{ + if (parent_) + parent_->addChild(this); + outSubDir_ = Generator::outputSubdir(); + if (operators_.isEmpty()) { + operators_.insert("++","inc"); + operators_.insert("--","dec"); + operators_.insert("==","eq"); + operators_.insert("!=","ne"); + operators_.insert("<<","lt-lt"); + operators_.insert(">>","gt-gt"); + operators_.insert("+=","plus-assign"); + operators_.insert("-=","minus-assign"); + operators_.insert("*=","mult-assign"); + operators_.insert("/=","div-assign"); + operators_.insert("%=","mod-assign"); + operators_.insert("&=","bitwise-and-assign"); + operators_.insert("|=","bitwise-or-assign"); + operators_.insert("^=","bitwise-xor-assign"); + operators_.insert("<<=","bitwise-left-shift-assign"); + operators_.insert(">>=","bitwise-right-shift-assign"); + operators_.insert("||","logical-or"); + operators_.insert("&&","logical-and"); + operators_.insert("()","call"); + operators_.insert("[]","subscript"); + operators_.insert("->","pointer"); + operators_.insert("->*","pointer-star"); + operators_.insert("+","plus"); + operators_.insert("-","minus"); + operators_.insert("*","mult"); + operators_.insert("/","div"); + operators_.insert("%","mod"); + operators_.insert("|","bitwise-or"); + operators_.insert("&","bitwise-and"); + operators_.insert("^","bitwise-xor"); + operators_.insert("!","not"); + operators_.insert("~","bitwise-not"); + operators_.insert("<=","lt-eq"); + operators_.insert(">=","gt-eq"); + operators_.insert("<","lt"); + operators_.insert(">","gt"); + operators_.insert("=","assign"); + operators_.insert(",","comma"); + operators_.insert("delete[]","delete-array"); + operators_.insert("delete","delete"); + operators_.insert("new[]","new-array"); + operators_.insert("new","new"); + } +} + +/*! \fn QString Node::url() const + Returns the node's URL. + */ + +/*! \fn void Node::setUrl(const QString &url) + Sets the node's URL to \a url + */ + +/*! + Returns this node's page type as a string, for use as an + attribute value in XML or HTML. + */ +QString Node::pageTypeString() const +{ + return pageTypeString((PageType) pageType_); +} + +/*! + Returns the page type \a t as a string, for use as an + attribute value in XML or HTML. + */ +QString Node::pageTypeString(unsigned char t) +{ + switch ((PageType)t) { + case Node::ApiPage: + return "api"; + case Node::ArticlePage: + return "article"; + case Node::ExamplePage: + return "example"; + case Node::HowToPage: + return "howto"; + case Node::OverviewPage: + return "overview"; + case Node::TutorialPage: + return "tutorial"; + case Node::FAQPage: + return "faq"; + case Node::DitaMapPage: + return "ditamap"; + default: + return "article"; + } +} + +/*! + Returns this node's type as a string for use as an + attribute value in XML or HTML. + */ +QString Node::nodeTypeString() const +{ + return nodeTypeString(type()); +} + +/*! + Returns the node type \a t as a string for use as an + attribute value in XML or HTML. + */ +QString Node::nodeTypeString(unsigned char t) +{ + switch ((NodeType)t) { + case Namespace: + return "namespace"; + case Class: + return "class"; + case Document: + return "document"; + case Enum: + return "enum"; + case Typedef: + return "typedef"; + case Function: + return "function"; + case Property: + return "property"; + case Variable: + return "variable"; + case Group: + return "group"; + case Module: + return "module"; + case QmlType: + return "QML type"; + case QmlBasicType: + return "QML basic type"; + case QmlModule: + return "QML module"; + case QmlProperty: + return "QML property"; + case QmlPropertyGroup: + return "QML property group"; + case QmlSignal: + return "QML signal"; + case QmlSignalHandler: + return "QML signal handler"; + case QmlMethod: + return "QML method"; + default: + break; + } + return QString(); +} + +/*! + Returns this node's subtype as a string for use as an + attribute value in XML or HTML. This is only useful + in the case where the node type is Document. + */ +QString Node::nodeSubtypeString() const +{ + return nodeSubtypeString(docSubtype()); +} + +/*! + Returns the node subtype \a t as a string for use as an + attribute value in XML or HTML. This is only useful + in the case where the node type is Document. + */ +QString Node::nodeSubtypeString(unsigned char t) +{ + switch ((DocSubtype)t) { + case Example: + return "example"; + case HeaderFile: + return "header file"; + case File: + return "file"; + case Image: + return "image"; + case Page: + return "page"; + case ExternalPage: + return "external page"; + case DitaMap: + return "ditamap"; + case NoSubtype: + default: + break; + } + return QString(); +} + +/*! + Set the page type according to the string \a t. + */ +void Node::setPageType(const QString& t) +{ + if ((t == "API") || (t == "api")) + pageType_ = (unsigned char) ApiPage; + else if (t == "howto") + pageType_ = (unsigned char) HowToPage; + else if (t == "overview") + pageType_ = (unsigned char) OverviewPage; + else if (t == "tutorial") + pageType_ = (unsigned char) TutorialPage; + else if (t == "faq") + pageType_ = (unsigned char) FAQPage; + else if (t == "article") + pageType_ = (unsigned char) ArticlePage; + else if (t == "example") + pageType_ = (unsigned char) ExamplePage; + else if (t == "ditamap") + pageType_ = (unsigned char) DitaMapPage; +} + +/*! Converts the boolean value \a b to an enum representation + of the boolean type, which includes an enum value for the + \e {default value} of the item, i.e. true, false, or default. + */ +Node::FlagValue Node::toFlagValue(bool b) +{ + return b ? FlagValueTrue : FlagValueFalse; +} + +/*! + Converts the enum \a fv back to a boolean value. + If \a fv is neither the true enum value nor the + false enum value, the boolean value returned is + \a defaultValue. + + Note that runtimeDesignabilityFunction() should be called + first. If that function returns the name of a function, it + means the function must be called at runtime to determine + whether the property is Designable. + */ +bool Node::fromFlagValue(FlagValue fv, bool defaultValue) +{ + switch (fv) { + case FlagValueTrue: + return true; + case FlagValueFalse: + return false; + default: + return defaultValue; + } +} + +/*! + Sets the pointer to the node that this node relates to. + */ +void Node::setRelates(Aggregate *pseudoParent) +{ + if (pseudoParent == parent()) + return; + + removeRelates(); + relatesTo_ = pseudoParent; + pseudoParent->addRelated(this); +} + +/*! + Sets the (unresolved) entity \a name that this node relates to. + */ +void Node::setRelates(const QString& name) +{ + removeRelates(); + // Create a dummy aggregate for writing the name into the index + relatesTo_ = new DocumentNode(0, name, Node::NoSubtype, Node::NoPageType); +} + +/*! + This function creates a pair that describes a link. + The pair is composed from \a link and \a desc. The + \a linkType is the map index the pair is filed under. + */ +void Node::setLink(LinkType linkType, const QString &link, const QString &desc) +{ + QPair<QString,QString> linkPair; + linkPair.first = link; + linkPair.second = desc; + linkMap_[linkType] = linkPair; +} + +/*! + Sets the information about the project and version a node was introduced + in. The string is simplified, removing excess whitespace before being + stored. +*/ +void Node::setSince(const QString &since) +{ + since_ = since.simplified(); +} + +/*! + Returns a string representing the access specifier. + */ +QString Node::accessString() const +{ + switch ((Access) access_) { + case Protected: + return "protected"; + case Private: + return "private"; + case Public: + default: + break; + } + return "public"; +} + +/*! + Extract a class name from the type \a string and return it. + */ +QString Node::extractClassName(const QString &string) const +{ + QString result; + for (int i=0; i<=string.size(); ++i) { + QChar ch; + if (i != string.size()) + ch = string.at(i); + + QChar lower = ch.toLower(); + if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || + ch.digitValue() >= 0 || + ch == QLatin1Char('_') || + ch == QLatin1Char(':')) { + result += ch; + } + else if (!result.isEmpty()) { + if (result != QLatin1String("const")) + return result; + result.clear(); + } + } + return result; +} + +/*! + Returns a string representing the access specifier. + */ +QString RelatedClass::accessString() const +{ + switch (access_) { + case Node::Protected: + return "protected"; + case Node::Private: + return "private"; + case Node::Public: + default: + break; + } + return "public"; +} + +/*! + Returns the inheritance status. + */ +Node::Status Node::inheritedStatus() const +{ + Status parentStatus = Active; + if (parent_) + parentStatus = parent_->inheritedStatus(); + return (Status)qMin((int)status_, (int)parentStatus); +} + +/*! + Returns the thread safeness value for whatever this node + represents. But if this node has a parent and the thread + safeness value of the parent is the same as the thread + safeness value of this node, what is returned is the + value \c{UnspecifiedSafeness}. Why? + */ +Node::ThreadSafeness Node::threadSafeness() const +{ + if (parent_ && (ThreadSafeness) safeness_ == parent_->inheritedThreadSafeness()) + return UnspecifiedSafeness; + return (ThreadSafeness) safeness_; +} + +/*! + If this node has a parent, the parent's thread safeness + value is returned. Otherwise, this node's thread safeness + value is returned. Why? + */ +Node::ThreadSafeness Node::inheritedThreadSafeness() const +{ + if (parent_ && (ThreadSafeness) safeness_ == UnspecifiedSafeness) + return parent_->inheritedThreadSafeness(); + return (ThreadSafeness) safeness_; +} + +#if 0 +/*! + Returns the sanitized file name without the path. + If the file is an html file, the html suffix + is removed. Why? + */ +QString Node::fileBase() const +{ + QString base = name(); + if (base.endsWith(".html")) + base.chop(5); + base.replace(QRegExp("[^A-Za-z0-9]+"), " "); + base = base.trimmed(); + base.replace(QLatin1Char(' '), QLatin1Char('-')); + return base.toLower(); +} +/*! + Returns this node's Universally Unique IDentifier as a + QString. Creates the UUID first, if it has not been created. + */ +QString Node::guid() const +{ + if (uuid_.isEmpty()) + uuid_ = idForNode(); + return uuid_; +} +#endif + +/*! + If this node is a QML or JS type node, return a pointer to + it. If it is a child of a QML or JS type node, return the + pointer to its parent QMLor JS type node. Otherwise return + 0; + */ +QmlTypeNode* Node::qmlTypeNode() +{ + if (isQmlNode() || isJsNode()) { + Node* n = this; + while (n && !(n->isQmlType() || n->isJsType())) + n = n->parent(); + if (n && (n->isQmlType() || n->isJsType())) + return static_cast<QmlTypeNode*>(n); + } + return 0; +} + +/*! + If this node is a QML node, find its QML class node, + and return a pointer to the C++ class node from the + QML class node. That pointer will be null if the QML + class node is a component. It will be non-null if + the QML class node is a QML element. + */ +ClassNode* Node::declarativeCppNode() +{ + QmlTypeNode* qcn = qmlTypeNode(); + if (qcn) + return qcn->classNode(); + return 0; +} + +/*! + Returns \c true if the node's status is Internal, or if its + parent is a class with internal status. + */ +bool Node::isInternal() const +{ + if (status() == Internal) + return true; + if (parent() && parent()->status() == Internal) + return true; + if (relates() && relates()->status() == Internal) + return true; + return false; +} + +/*! + Returns a pointer to the Tree this node is in. + */ +Tree* Node::tree() const +{ + return (parent() ? parent()->tree() : 0); +} + +/*! + Returns a pointer to the root of the Tree this node is in. + */ +const Node* Node::root() const +{ + return (parent() ? parent()->root() : this); +} + +/*! + Sets the node's declaration location, its definition + location, or both, depending on the suffix of the file + name from the file path in location \a t. + */ +void Node::setLocation(const Location& t) +{ + QString suffix = t.fileSuffix(); + if (suffix == "h") + declLocation_ = t; + else if (suffix == "cpp") + defLocation_ = t; + else { + declLocation_ = t; + defLocation_ = t; + } +} + +/*! + \class Aggregate + */ + +/*! + The inner node destructor deletes the children and removes + this node from its related nodes. + */ +Aggregate::~Aggregate() +{ + removeFromRelated(); + deleteChildren(); +} + +/*! + If \a genus is \c{Node::DontCare}, find the first node in + this node's child list that has the given \a name. If this + node is a QML type, be sure to also look in the children + of its property group nodes. Return the matching node or 0. + + If \a genus is either \c{Node::CPP} or \c {Node::QML}, then + find all this node's children that have the given \a name, + and return the one that satisfies the \a genus requirement. + */ +Node *Aggregate::findChildNode(const QString& name, Node::Genus genus) const +{ + if (genus == Node::DontCare) { + Node *node = childMap_.value(name); + if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group? + return node; + if (isQmlType() || isJsType()) { + for (int i=0; i<children_.size(); ++i) { + Node* n = children_.at(i); + if (n->isQmlPropertyGroup() || isJsPropertyGroup()) { + node = static_cast<Aggregate*>(n)->findChildNode(name, genus); + if (node) + return node; + } + } + } + } + else { + NodeList nodes = childMap_.values(name); + if (!nodes.isEmpty()) { + for (int i=0; i<nodes.size(); ++i) { + Node* node = nodes.at(i); + if (genus == node->genus()) + return node; + } + } + } + return primaryFunctionMap_.value(name); +} + +/*! + Find all the child nodes of this node that are named + \a name and return them in \a nodes. + */ +void Aggregate::findChildren(const QString& name, NodeList& nodes) const +{ + nodes = childMap_.values(name); + Node* n = primaryFunctionMap_.value(name); + if (n) { + nodes.append(n); + NodeList t = secondaryFunctionMap_.value(name); + if (!t.isEmpty()) + nodes.append(t); + } + if (!nodes.isEmpty() || !(isQmlNode() || isJsNode())) + return; + int i = name.indexOf(QChar('.')); + if (i < 0) + return; + QString qmlPropGroup = name.left(i); + NodeList t = childMap_.values(qmlPropGroup); + if (t.isEmpty()) + return; + foreach (Node* n, t) { + if (n->isQmlPropertyGroup() || n->isJsPropertyGroup()) { + n->findChildren(name, nodes); + if (!nodes.isEmpty()) + break; + } + } +} + +/*! + This function is like findChildNode(), but if a node + with the specified \a name is found but it is not of the + specified \a type, 0 is returned. + */ +Node* Aggregate::findChildNode(const QString& name, NodeType type) +{ + if (type == Function) + return primaryFunctionMap_.value(name); + else { + NodeList nodes = childMap_.values(name); + for (int i=0; i<nodes.size(); ++i) { + Node* node = nodes.at(i); + if (node->type() == type) + return node; + } + } + return 0; +} + +/*! + Find a function node that is a child of this nose, such + that the function node has the specified \a name. + */ +FunctionNode *Aggregate::findFunctionNode(const QString& name, const QString& params) const +{ + FunctionNode* pfn = static_cast<FunctionNode*>(primaryFunctionMap_.value(name)); + FunctionNode* fn = pfn; + if (fn) { + const QVector<Parameter>* funcParams = &(fn->parameters()); + if (params.isEmpty() && funcParams->isEmpty()) + return fn; + bool isQPrivateSignal = false; // Not used in the search + QVector<Parameter> testParams; + if (!params.isEmpty()) { + CppCodeParser* cppParser = CppCodeParser::cppParser(); + cppParser->parseParameters(params, testParams, isQPrivateSignal); + } + NodeList funcs = secondaryFunctionMap_.value(name); + int i = -1; + while (fn) { + if (testParams.size() == funcParams->size()) { + if (testParams.isEmpty()) + return fn; + bool different = false; + for (int j=0; j<testParams.size(); j++) { + if (testParams.at(j).dataType() != funcParams->at(j).dataType()) { + different = true; + break; + } + } + if (!different) + return fn; + } + if (++i < funcs.size()) { + fn = static_cast<FunctionNode*>(funcs.at(i)); + funcParams = &(fn->parameters()); + } + else + fn = 0; + } + if (!fn && !testParams.empty()) + return 0; + } + /* + Most \l commands that link to functions don't include + the parameter declarations in the function signature, + so if the \l is meant to go to a function that does + have parameters, the algorithm above won't find it. + Therefore we must return the pointer to the function + in the primary function map in the cases where the + parameters should have been specified in the \l command. + */ + return (fn ? fn : pfn); +} + +/*! + Find the function node that is a child of this node, such + that the function has the same name and signature as the + \a clone node. + */ +FunctionNode *Aggregate::findFunctionNode(const FunctionNode *clone) const +{ + QMap<QString,Node*>::ConstIterator c = primaryFunctionMap_.constFind(clone->name()); + if (c != primaryFunctionMap_.constEnd()) { + if (isSameSignature(clone, (FunctionNode *) *c)) { + return (FunctionNode *) *c; + } + else if (secondaryFunctionMap_.contains(clone->name())) { + const NodeList& secs = secondaryFunctionMap_[clone->name()]; + NodeList::ConstIterator s = secs.constBegin(); + while (s != secs.constEnd()) { + if (isSameSignature(clone, (FunctionNode *) *s)) + return (FunctionNode *) *s; + ++s; + } + } + } + return 0; +} + +/*! + Returns the list of keys from the primary function map. + */ +QStringList Aggregate::primaryKeys() +{ + QStringList t; + QMap<QString, Node*>::iterator i = primaryFunctionMap_.begin(); + while (i != primaryFunctionMap_.end()) { + t.append(i.key()); + ++i; + } + return t; +} + +/*! + Returns the list of keys from the secondary function map. + */ +QStringList Aggregate::secondaryKeys() +{ + QStringList t; + QMap<QString, NodeList>::iterator i = secondaryFunctionMap_.begin(); + while (i != secondaryFunctionMap_.end()) { + t.append(i.key()); + ++i; + } + return t; +} + +/*! + Mark all child nodes that have no documentation as having + private access and internal status. qdoc will then ignore + them for documentation purposes. Some nodes have an + Intermediate status, meaning that they should be ignored, + but not their children. + */ +void Aggregate::makeUndocumentedChildrenInternal() +{ + foreach (Node *child, childNodes()) { + if (child->doc().isEmpty() && child->status() != Node::Intermediate) { + child->setAccess(Node::Private); + child->setStatus(Node::Internal); + } + } +} + +/*! + This is where we set the overload numbers for function nodes. + \note Overload numbers for related non-members are handled + separately. + */ +void Aggregate::normalizeOverloads() +{ + QMap<QString, Node *>::Iterator p1 = primaryFunctionMap_.begin(); + while (p1 != primaryFunctionMap_.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p1; + if (primaryFunc->status() != Active || primaryFunc->access() == Private) { + if (secondaryFunctionMap_.contains(primaryFunc->name())) { + /* + Either the primary function is not active or it is private. + It therefore can't be the primary function. Search the list + of overloads to find one that can be the primary function. + */ + NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()]; + NodeList::ConstIterator s = overloads.constBegin(); + while (s != overloads.constEnd()) { + FunctionNode *overloadFunc = (FunctionNode *) *s; + /* + Any non-obsolete, non-private function (i.e., visible function) + is preferable to the current primary function. Swap the primary + and overload functions. + */ + if (overloadFunc->status() == Active && overloadFunc->access() != Private) { + primaryFunc->setOverloadNumber(overloadFunc->overloadNumber()); + overloads.replace(overloads.indexOf(overloadFunc), primaryFunc); + *p1 = overloadFunc; + overloadFunc->setOverloadFlag(false); + overloadFunc->setOverloadNumber(0); + break; + } + ++s; + } + } + } + ++p1; + } + /* + Ensure that none of the primary functions is marked with \overload. + */ + QMap<QString, Node *>::Iterator p = primaryFunctionMap_.begin(); + while (p != primaryFunctionMap_.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p; + if (primaryFunc->isOverload()) { + if (secondaryFunctionMap_.contains(primaryFunc->name())) { + /* + The primary function is marked with \overload. Find an + overload in the secondary function map that is not marked + with \overload but that is active and not private. Then + swap it with the primary function. + */ + NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()]; + NodeList::ConstIterator s = overloads.constBegin(); + while (s != overloads.constEnd()) { + FunctionNode *overloadFunc = (FunctionNode *) *s; + if (!overloadFunc->isOverload()) { + if (overloadFunc->status() == Active && overloadFunc->access() != Private) { + primaryFunc->setOverloadNumber(overloadFunc->overloadNumber()); + overloads.replace(overloads.indexOf(overloadFunc), primaryFunc); + *p = overloadFunc; + overloadFunc->setOverloadFlag(false); + overloadFunc->setOverloadNumber(0); + break; + } + } + ++s; + } + } + } + ++p; + } + /* + Recursive part. + */ + NodeList::ConstIterator c = childNodes().constBegin(); + while (c != childNodes().constEnd()) { + if ((*c)->isAggregate()) + ((Aggregate *) *c)->normalizeOverloads(); + ++c; + } +} + +/*! + */ +void Aggregate::removeFromRelated() +{ + while (!related_.isEmpty()) { + Node *p = static_cast<Node *>(related_.takeFirst()); + + if (p != 0 && p->relates() == this) p->clearRelated(); + } +} + +/*! + Deletes all this node's children. + */ +void Aggregate::deleteChildren() +{ + NodeList childrenCopy = children_; + // Clear internal collections before deleting child nodes + children_.clear(); + childMap_.clear(); + enumChildren_.clear(); + primaryFunctionMap_.clear(); + secondaryFunctionMap_.clear(); + qDeleteAll(childrenCopy); +} + +/*! \fn bool Aggregate::isAggregate() const + Returns \c true because this is an inner node. + */ + +/*! + Returns \c true if the node is a class node or a QML type node + that is marked as being a wrapper class or QML type, or if + it is a member of a wrapper class or type. + */ +bool Node::isWrapper() const +{ + return (parent_ ? parent_->isWrapper() : false); +} + +/*! + Finds the enum type node that has \a enumValue as one of + its enum values and returns a pointer to it. Returns 0 if + no enum type node is found that has \a enumValue as one + of its values. + */ +const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const +{ + foreach (const Node *node, enumChildren_) { + const EnumNode *en = static_cast<const EnumNode *>(node); + if (en->hasItem(enumValue)) + return en; + } + return 0; +} + +/*! + Returns a node list containing all the member functions of + some class such that the functions overload the name \a funcName. + */ +NodeList Aggregate::overloads(const QString &funcName) const +{ + NodeList result; + Node *primary = primaryFunctionMap_.value(funcName); + if (primary) { + result << primary; + result += secondaryFunctionMap_[funcName]; + } + return result; +} + +/*! + Construct an inner node (i.e., not a leaf node) of the + given \a type and having the given \a parent and \a name. + */ +Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString& name) + : Node(type, parent, name) +{ + switch (type) { + case Class: + case QmlType: + case Namespace: + setPageType(ApiPage); + break; + default: + break; + } +} + +/*! + Appends an \a include file to the list of include files. + */ +void Aggregate::addInclude(const QString& include) +{ + includes_.append(include); +} + +/*! + Sets the list of include files to \a includes. + */ +void Aggregate::setIncludes(const QStringList& includes) +{ + includes_ = includes; +} + +/*! + f1 is always the clone + */ +bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) +{ + if (f1->parameters().size() != f2->parameters().size()) + return false; + if (f1->isConst() != f2->isConst()) + return false; + + QVector<Parameter>::ConstIterator p1 = f1->parameters().constBegin(); + QVector<Parameter>::ConstIterator p2 = f2->parameters().constBegin(); + while (p2 != f2->parameters().constEnd()) { + if ((*p1).hasType() && (*p2).hasType()) { + if ((*p1).rightType() != (*p2).rightType()) + return false; + + QString t1 = p1->dataType(); + QString t2 = p2->dataType(); + + if (t1.length() < t2.length()) + qSwap(t1, t2); + + /* + ### hack for C++ to handle superfluous + "Foo::" prefixes gracefully + */ + if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) + return false; + } + ++p1; + ++p2; + } + return true; +} + +/*! + Adds the \a child to this node's child list. It might also + be necessary to update this node's internal collections and + the child's parent pointer and output subdirectory. + */ +void Aggregate::addChild(Node *child) +{ + children_.append(child); + if (child->type() == Function + || child->type() == QmlMethod + || child->type() == QmlSignal) { + FunctionNode *func = static_cast<FunctionNode*>(child); + QString name = func->name(); + if (!primaryFunctionMap_.contains(name)) { + primaryFunctionMap_.insert(name, func); + func->setOverloadNumber(0); + } + else { + NodeList &overloads = secondaryFunctionMap_[name]; + overloads.append(func); + func->setOverloadNumber(overloads.size()); + } + } + else { + if (child->type() == Enum) + enumChildren_.append(child); + childMap_.insertMulti(child->name(), child); + } + if (child->parent() == 0) { + child->setParent(this); + child->setOutputSubdirectory(this->outputSubdirectory()); + } +} + +/*! + Adds the \a child to this node's child map using \a title + as the key. The \a child is not added to the child list + again, because it is presumed to already be there. We just + want to be able to find the child by its \a title. + */ +void Aggregate::addChild(Node* child, const QString& title) +{ + childMap_.insertMulti(title, child); +} + +/*! + The \a child is removed from this node's child list and + from this node's internal collections. The child's parent + pointer is set to 0, but its output subdirectory is not + changed. + */ +void Aggregate::removeChild(Node *child) +{ + children_.removeAll(child); + enumChildren_.removeAll(child); + if (child->type() == Function + || child->type() == QmlMethod + || child->type() == QmlSignal) { + QMap<QString, Node *>::Iterator primary = primaryFunctionMap_.find(child->name()); + NodeList& overloads = secondaryFunctionMap_[child->name()]; + if (primary != primaryFunctionMap_.end() && *primary == child) { + primaryFunctionMap_.erase(primary); + if (!overloads.isEmpty()) { + FunctionNode* fn = static_cast<FunctionNode*>(overloads.takeFirst()); + fn->setOverloadNumber(0); + primaryFunctionMap_.insert(child->name(), fn); + } + } + else + overloads.removeAll(child); + } + QMap<QString, Node *>::Iterator ent = childMap_.find(child->name()); + while (ent != childMap_.end() && ent.key() == child->name()) { + if (*ent == child) { + childMap_.erase(ent); + break; + } + ++ent; + } + if (child->title().isEmpty()) + return; + ent = childMap_.find(child->title()); + while (ent != childMap_.end() && ent.key() == child->title()) { + if (*ent == child) { + childMap_.erase(ent); + break; + } + ++ent; + } + child->setParent(0); +} + +/*! + Recursively sets the output subdirectory for children + */ +void Aggregate::setOutputSubdirectory(const QString &t) +{ + Node::setOutputSubdirectory(t); + for (int i = 0; i < childNodes().size(); ++i) + childNodes().at(i)->setOutputSubdirectory(t); +} + +/*! + Find the module (Qt Core, Qt GUI, etc.) to which the class belongs. + We do this by obtaining the full path to the header file's location + and examine everything between "src/" and the filename. This is + semi-dirty because we are assuming a particular directory structure. + + This function is only really useful if the class's module has not + been defined in the header file with a QT_MODULE macro or with an + \inmodule command in the documentation. +*/ +QString Node::physicalModuleName() const +{ + if (!physicalModuleName_.isEmpty()) + return physicalModuleName_; + + QString path = location().filePath(); + QString pattern = QString("src") + QDir::separator(); + int start = path.lastIndexOf(pattern); + + if (start == -1) + return QString(); + + QString moduleDir = path.mid(start + pattern.size()); + int finish = moduleDir.indexOf(QDir::separator()); + + if (finish == -1) + return QString(); + + QString physicalModuleName = moduleDir.left(finish); + + if (physicalModuleName == "corelib") + return "QtCore"; + else if (physicalModuleName == "uitools") + return "QtUiTools"; + else if (physicalModuleName == "gui") + return "QtGui"; + else if (physicalModuleName == "network") + return "QtNetwork"; + else if (physicalModuleName == "opengl") + return "QtOpenGL"; + else if (physicalModuleName == "svg") + return "QtSvg"; + else if (physicalModuleName == "sql") + return "QtSql"; + else if (physicalModuleName == "qtestlib") + return "QtTest"; + else if (moduleDir.contains("webkit")) + return "QtWebKit"; + else if (physicalModuleName == "xml") + return "QtXml"; + else + return QString(); +} + +/*! + Removes a node from the list of nodes related to this one. + If it is a function node, also remove from the primary/ + secondary function maps. + */ +void Aggregate::removeRelated(Node *pseudoChild) +{ + related_.removeAll(pseudoChild); + + if (pseudoChild->isFunction()) { + QMap<QString, Node *>::Iterator p = primaryFunctionMap_.find(pseudoChild->name()); + while (p != primaryFunctionMap_.end()) { + if (p.value() == pseudoChild) { + primaryFunctionMap_.erase(p); + break; + } + ++p; + } + NodeList& overloads = secondaryFunctionMap_[pseudoChild->name()]; + overloads.removeAll(pseudoChild); + } +} + +/*! + Adds \a pseudoChild to the list of nodes related to this one. Resolve a correct + overload number for a related non-member function. + */ +void Aggregate::addRelated(Node *pseudoChild) +{ + related_.append(pseudoChild); + + if (pseudoChild->isFunction()) { + FunctionNode* fn = static_cast<FunctionNode*>(pseudoChild); + if (primaryFunctionMap_.contains(pseudoChild->name())) { + secondaryFunctionMap_[pseudoChild->name()].append(pseudoChild); + fn->setOverloadNumber(secondaryFunctionMap_[pseudoChild->name()].size()); + fn->setOverloadFlag(true); + } + else { + primaryFunctionMap_.insert(pseudoChild->name(), pseudoChild); + fn->setOverloadNumber(0); + fn->setOverloadFlag(false); + } + } +} + +/*! + If this node has a child that is a QML property named \a n, + return the pointer to that child. + */ +QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n) const +{ + foreach (Node* child, childNodes()) { + if (child->type() == Node::QmlProperty) { + if (child->name() == n) + return static_cast<QmlPropertyNode*>(child); + } + else if (child->isQmlPropertyGroup()) { + QmlPropertyNode* t = child->hasQmlProperty(n); + if (t) + return t; + } + } + return 0; +} + +/*! + If this node has a child that is a QML property named \a n + whose type (attached or normal property) matches \a attached, + return the pointer to that child. + */ +QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n, bool attached) const +{ + foreach (Node* child, childNodes()) { + if (child->type() == Node::QmlProperty) { + if (child->name() == n && child->isAttached() == attached) + return static_cast<QmlPropertyNode*>(child); + } + else if (child->isQmlPropertyGroup()) { + QmlPropertyNode* t = child->hasQmlProperty(n, attached); + if (t) + return t; + } + } + return 0; +} + +/*! + \class LeafNode + */ + +/*! \fn bool LeafNode::isAggregate() const + Returns \c false because this is a LeafNode. + */ + +/*! + Constructs a leaf node named \a name of the specified + \a type. The new leaf node becomes a child of \a parent. + */ +LeafNode::LeafNode(NodeType type, Aggregate *parent, const QString& name) + : Node(type, parent, name) +{ + switch (type) { + case Enum: + case Function: + case Typedef: + case Variable: + case QmlProperty: + case QmlSignal: + case QmlSignalHandler: + case QmlMethod: + case QmlBasicType: + setPageType(ApiPage); + break; + default: + break; + } +} + +/*! + This constructor should only be used when this node's parent + is meant to be \a parent, but this node is not to be listed + as a child of \a parent. It is currently only used for the + documentation case where a \e{qmlproperty} command is used + to override the QML definition of a QML property. + */ +LeafNode::LeafNode(Aggregate* parent, NodeType type, const QString& name) + : Node(type, 0, name) +{ + setParent(parent); + switch (type) { + case Enum: + case Function: + case Typedef: + case Variable: + case QmlProperty: + case QmlSignal: + case QmlSignalHandler: + case QmlMethod: + setPageType(ApiPage); + break; + default: + break; + } +} + + +/*! + \class NamespaceNode + */ + +/*! + Constructs a namespace node. + */ +NamespaceNode::NamespaceNode(Aggregate *parent, const QString& name) + : Aggregate(Namespace, parent, name), seen_(false), tree_(0) +{ + setGenus(Node::CPP); + setPageType(ApiPage); +} + +/*! + \class ClassNode + \brief This class represents a C++ class. + */ + +/*! + Constructs a class node. A class node will generate an API page. + */ +ClassNode::ClassNode(Aggregate *parent, const QString& name) + : Aggregate(Class, parent, name) +{ + abstract_ = false; + wrapper_ = false; + qmlelement = 0; + setGenus(Node::CPP); + setPageType(ApiPage); +} + +/*! + Adds the base class \a node to this class's list of base + classes. The base class has the specified \a access. This + is a resolved base class. + */ +void ClassNode::addResolvedBaseClass(Access access, ClassNode* node) +{ + bases_.append(RelatedClass(access, node)); + node->derived_.append(RelatedClass(access, this)); +} + +/*! + Adds the derived class \a node to this class's list of derived + classes. The derived class inherits this class with \a access. + */ +void ClassNode::addDerivedClass(Access access, ClassNode* node) +{ + derived_.append(RelatedClass(access, node)); +} + +/*! + Add an unresolved base class to this class node's list of + base classes. The unresolved base class will be resolved + before the generate phase of qdoc. In an unresolved base + class, the pointer to the base class node is 0. + */ +void ClassNode::addUnresolvedBaseClass(Access access, + const QStringList& path, + const QString& signature) +{ + bases_.append(RelatedClass(access, path, signature)); +} + +/*! + Add an unresolved \c using clause to this class node's list + of \c using clauses. The unresolved \c using clause will be + resolved before the generate phase of qdoc. In an unresolved + \c using clause, the pointer to the function node is 0. + */ +void ClassNode::addUnresolvedUsingClause(const QString& signature) +{ + usingClauses_.append(UsingClause(signature)); +} + +/*! + */ +void ClassNode::fixBaseClasses() +{ + int i; + i = 0; + QSet<ClassNode *> found; + + // Remove private and duplicate base classes. + while (i < bases_.size()) { + ClassNode* bc = bases_.at(i).node_; + if (bc && (bc->access() == Node::Private || found.contains(bc))) { + RelatedClass rc = bases_.at(i); + bases_.removeAt(i); + ignoredBases_.append(rc); + const QList<RelatedClass> &bb = bc->baseClasses(); + for (int j = bb.size() - 1; j >= 0; --j) + bases_.insert(i, bb.at(j)); + } + else { + ++i; + } + found.insert(bc); + } + + i = 0; + while (i < derived_.size()) { + ClassNode* dc = derived_.at(i).node_; + if (dc && dc->access() == Node::Private) { + derived_.removeAt(i); + const QList<RelatedClass> &dd = dc->derivedClasses(); + for (int j = dd.size() - 1; j >= 0; --j) + derived_.insert(i, dd.at(j)); + } + else { + ++i; + } + } +} + +/*! + Not sure why this is needed. + */ +void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn) +{ + QList<RelatedClass>::const_iterator bc = baseClasses().constBegin(); + while (bc != baseClasses().constEnd()) { + ClassNode* cn = bc->node_; + if (cn) { + Node* n = cn->findChildNode(pn->name(), Node::Property); + if (n) { + PropertyNode* baseProperty = static_cast<PropertyNode*>(n); + cn->fixPropertyUsingBaseClasses(baseProperty); + pn->setOverriddenFrom(baseProperty); + } + else + cn->fixPropertyUsingBaseClasses(pn); + } + ++bc; + } +} + +/*! + Search the child list to find the property node with the + specified \a name. + */ +PropertyNode* ClassNode::findPropertyNode(const QString& name) +{ + Node* n = findChildNode(name, Node::Property); + + if (n) + return static_cast<PropertyNode*>(n); + + PropertyNode* pn = 0; + + const QList<RelatedClass> &bases = baseClasses(); + if (!bases.isEmpty()) { + for (int i = 0; i < bases.size(); ++i) { + ClassNode* cn = bases[i].node_; + if (cn) { + pn = cn->findPropertyNode(name); + if (pn) + break; + } + } + } + const QList<RelatedClass>& ignoredBases = ignoredBaseClasses(); + if (!ignoredBases.isEmpty()) { + for (int i = 0; i < ignoredBases.size(); ++i) { + ClassNode* cn = ignoredBases[i].node_; + if (cn) { + pn = cn->findPropertyNode(name); + if (pn) + break; + } + } + } + + return pn; +} + +/*! + This function does a recursive search of this class node's + base classes looking for one that has a QML element. If it + finds one, it returns the pointer to that QML element. If + it doesn't find one, it returns null. + */ +QmlTypeNode* ClassNode::findQmlBaseNode() +{ + QmlTypeNode* result = 0; + const QList<RelatedClass>& bases = baseClasses(); + + if (!bases.isEmpty()) { + for (int i = 0; i < bases.size(); ++i) { + ClassNode* cn = bases[i].node_; + if (cn && cn->qmlElement()) { + return cn->qmlElement(); + } + } + for (int i = 0; i < bases.size(); ++i) { + ClassNode* cn = bases[i].node_; + if (cn) { + result = cn->findQmlBaseNode(); + if (result != 0) { + return result; + } + } + } + } + return result; +} + +/*! + \class DocumentNode + */ + +/*! + The type of a DocumentNode is Document, and it has a \a subtype, + which specifies the type of DocumentNode. The page type for + the page index is set here. + */ +DocumentNode::DocumentNode(Aggregate* parent, const QString& name, DocSubtype subtype, Node::PageType ptype) + : Aggregate(Document, parent, name), nodeSubtype_(subtype) +{ + setGenus(Node::DOC); + switch (subtype) { + case Page: + setPageType(ptype); + break; + case DitaMap: + setPageType(ptype); + break; + case Example: + setPageType(ExamplePage); + break; + default: + break; + } +} + +/*! \fn QString DocumentNode::title() const + Returns the document node's title. This is used for the page title. +*/ + +/*! + Sets the document node's \a title. This is used for the page title. + */ +void DocumentNode::setTitle(const QString &title) +{ + title_ = title; + parent()->addChild(this, title); +} + +/*! + Returns the document node's full title, which is usually + just title(), but for some DocSubtype values is different + from title() + */ +QString DocumentNode::fullTitle() const +{ + if (nodeSubtype_ == File) { + if (title().isEmpty()) + return name().mid(name().lastIndexOf('/') + 1) + " Example File"; + else + return title(); + } + else if (nodeSubtype_ == Image) { + if (title().isEmpty()) + return name().mid(name().lastIndexOf('/') + 1) + " Image File"; + else + return title(); + } + else if (nodeSubtype_ == HeaderFile) { + if (title().isEmpty()) + return name(); + else + return name() + " - " + title(); + } + else { + return title(); + } +} + +/*! + Returns the subtitle. + */ +QString DocumentNode::subTitle() const +{ + if (!subtitle_.isEmpty()) + return subtitle_; + + if ((nodeSubtype_ == File) || (nodeSubtype_ == Image)) { + if (title().isEmpty() && name().contains(QLatin1Char('/'))) + return name(); + } + return QString(); +} + +/*! + \class EnumNode + */ + +/*! + The constructor for the node representing an enum type + has a \a parent class and an enum type \a name. + */ +EnumNode::EnumNode(Aggregate *parent, const QString& name) + : LeafNode(Enum, parent, name), flagsType_(0) +{ + setGenus(Node::CPP); +} + +/*! + Add \a item to the enum type's item list. + */ +void EnumNode::addItem(const EnumItem& item) +{ + items_.append(item); + names_.insert(item.name()); +} + +/*! + Returns the access level of the enumeration item named \a name. + Apparently it is private if it has been omitted by qdoc's + omitvalue command. Otherwise it is public. + */ +Node::Access EnumNode::itemAccess(const QString &name) const +{ + if (doc().omitEnumItemNames().contains(name)) + return Private; + return Public; +} + +/*! + Returns the enum value associated with the enum \a name. + */ +QString EnumNode::itemValue(const QString &name) const +{ + foreach (const EnumItem &item, items_) { + if (item.name() == name) + return item.value(); + } + return QString(); +} + +/*! + \class TypedefNode + */ + +/*! + */ +TypedefNode::TypedefNode(Aggregate *parent, const QString& name) + : LeafNode(Typedef, parent, name), associatedEnum_(0) +{ + setGenus(Node::CPP); +} + +/*! + */ +void TypedefNode::setAssociatedEnum(const EnumNode *enume) +{ + associatedEnum_ = enume; +} + +/*! + \class Parameter + \brief The class Parameter contains one parameter. + + A parameter can be a function parameter or a macro + parameter. + */ + +/*! + Constructs this parameter from the left and right types + \a dataType and rightType, the parameter \a name, and the + \a defaultValue. In practice, \a rightType is not used, + and I don't know what is was meant for. + */ +Parameter::Parameter(const QString& dataType, + const QString& rightType, + const QString& name, + const QString& defaultValue) + : dataType_(dataType), + rightType_(rightType), + name_(name), + defaultValue_(defaultValue) +{ + // nothing. +} + +/*! + Standard copy constructor copies \p. + */ +Parameter::Parameter(const Parameter& p) + : dataType_(p.dataType_), + rightType_(p.rightType_), + name_(p.name_), + defaultValue_(p.defaultValue_) +{ + // nothing. +} + +/*! + standard assignment operator assigns \p. + */ +Parameter& Parameter::operator=(const Parameter& p) +{ + dataType_ = p.dataType_; + rightType_ = p.rightType_; + name_ = p.name_; + defaultValue_ = p.defaultValue_; + return *this; +} + +/*! + Reconstructs the text describing the parameter and + returns it. If \a value is true, the default value + will be included, if there is one. + */ +QString Parameter::reconstruct(bool value) const +{ + QString p = dataType_ + rightType_; + if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' '))) + p += QLatin1Char(' '); + p += name_; + if (value && !defaultValue_.isEmpty()) + p += " = " + defaultValue_; + return p; +} + + +/*! + \class FunctionNode + */ + +/*! + Construct a function node for a C++ function. It's parent + is \a parent, and it's name is \a name. + + Do not set overloadNumber_ in the initializer list because it + is set by addChild() in the Node base class. + */ +FunctionNode::FunctionNode(Aggregate *parent, const QString& name) + : LeafNode(Function, parent, name), + metaness_(Plain), + virtualness_(NonVirtual), + const_(false), + static_(false), + reimplemented_(false), + attached_(false), + privateSignal_(false), + overload_(false), + reimplementedFrom_(0) +{ + setGenus(Node::CPP); +} + +/*! + Construct a function node for a QML method or signal, specified + by \a type. It's parent is \a parent, and it's name is \a name. + If \a attached is true, it is an attached method or signal. + + Do not set overloadNumber_ in the initializer list because it + is set by addChild() in the Node base class. + */ +FunctionNode::FunctionNode(NodeType type, Aggregate *parent, const QString& name, bool attached) + : LeafNode(type, parent, name), + metaness_(Plain), + virtualness_(NonVirtual), + const_(false), + static_(false), + reimplemented_(false), + attached_(attached), + privateSignal_(false), + overload_(false), + reimplementedFrom_(0) +{ + setGenus(Node::QML); + if (type == QmlMethod || type == QmlSignal) { + if (name.startsWith("__")) + setStatus(Internal); + } + else if (type == Function) + setGenus(Node::CPP); +} + +/*! + Sets the \a virtualness of this function. If the \a virtualness + is PureVirtual, and if the parent() is a ClassNode, set the parent's + \e abstract flag to true. + */ +void FunctionNode::setVirtualness(Virtualness v) +{ + virtualness_ = v; + if ((v == PureVirtual) && parent() && (parent()->type() == Node::Class)) + parent()->setAbstract(true); +} + +/*! \fn void FunctionNode::setOverloadFlag(bool b) + Sets this function node's overload flag to \a b. + It does not set the overload number. + */ + +/*! \fn void FunctionNode::setOverloadNumber(unsigned char n) + Sets this function node's overload number to \a n. + It does not set the overload flag. + */ + +/*! + Sets the function node's reimplementation flag to \a b. + When \a b is true, it is supposed to mean that this function + is a reimplementation of a virtual function in a base class, + but it really just means the \e {\\reimp} command was seen in + the qdoc comment. + */ +void FunctionNode::setReimplemented(bool b) +{ + reimplemented_ = b; +} + +/*! + Append \a parameter to the parameter list. + */ +void FunctionNode::addParameter(const Parameter& parameter) +{ + parameters_.append(parameter); +} + +/*! + */ +void FunctionNode::borrowParameterNames(const FunctionNode *source) +{ + QVector<Parameter>::Iterator t = parameters_.begin(); + QVector<Parameter>::ConstIterator s = source->parameters_.constBegin(); + while (s != source->parameters_.constEnd() && t != parameters_.end()) { + if (!(*s).name().isEmpty()) + (*t).setName((*s).name()); + ++s; + ++t; + } +} + +/*! + If this function is a reimplementation, \a from points + to the FunctionNode of the function being reimplemented. + */ +void FunctionNode::setReimplementedFrom(FunctionNode *f) +{ + reimplementedFrom_ = f; + f->reimplementedBy_.append(this); +} + +/*! + Adds the "associated" property \a p to this function node. + The function might be the setter or getter for a property, + for example. + */ +void FunctionNode::addAssociatedProperty(PropertyNode *p) +{ + associatedProperties_.append(p); +} + +/*! + Returns true if this function has at least one property + that is active, i.e. at least one property that is not + obsolete. + */ +bool FunctionNode::hasActiveAssociatedProperty() const +{ + if (associatedProperties_.isEmpty()) + return false; + foreach (const PropertyNode* p, associatedProperties_) { + if (!p->isObsolete()) + return true; + } + return false; +} + +/*! \fn unsigned char FunctionNode::overloadNumber() const + Returns the overload number for this function. + */ + +/*! + Returns the list of parameter names. + */ +QStringList FunctionNode::parameterNames() const +{ + QStringList names; + QVector<Parameter>::ConstIterator p = parameters().constBegin(); + while (p != parameters().constEnd()) { + names << (*p).name(); + ++p; + } + return names; +} + +/*! + Returns a raw list of parameters. If \a names is true, the + names are included. If \a values is true, the default values + are included, if any are present. + */ +QString FunctionNode::rawParameters(bool names, bool values) const +{ + QString raw; + foreach (const Parameter ¶meter, parameters()) { + raw += parameter.dataType() + parameter.rightType(); + if (names) + raw += parameter.name(); + if (values) + raw += parameter.defaultValue(); + } + return raw; +} + +/*! + Returns the list of reconstructed parameters. If \a values + is true, the default values are included, if any are present. + */ +QStringList FunctionNode::reconstructParameters(bool values) const +{ + QStringList reconstructedParameters; + QVector<Parameter>::ConstIterator p = parameters().constBegin(); + while (p != parameters().constEnd()) { + reconstructedParameters << (*p).reconstruct(values); + ++p; + } + return reconstructedParameters; +} + +/*! + Reconstructs and returns the function's signature. If \a values + is true, the default values of the parameters are included, if + present. + */ +QString FunctionNode::signature(bool values) const +{ + QString s; + if (!returnType().isEmpty()) + s = returnType() + QLatin1Char(' '); + s += name() + QLatin1Char('('); + QStringList reconstructedParameters = reconstructParameters(values); + int p = reconstructedParameters.size(); + if (p > 0) { + for (int i=0; i<p; i++) { + s += reconstructedParameters[i]; + if (i < (p-1)) + s += ", "; + } + } + s += QLatin1Char(')'); + return s; +} + +/*! + Returns true if function \a fn has role \a r for this + property. + */ +PropertyNode::FunctionRole PropertyNode::role(const FunctionNode* fn) const +{ + for (int i=0; i<4; i++) { + if (functions_[i].contains(const_cast<FunctionNode*>(fn))) + return (FunctionRole) i; + } + return Notifier; +} + +/*! + Print some debugging stuff. + */ +void FunctionNode::debug() const +{ + qDebug("QML METHOD %s returnType_ %s parentPath_ %s", + qPrintable(name()), qPrintable(returnType_), qPrintable(parentPath_.join(' '))); +} + +/*! + \class PropertyNode + + This class describes one instance of using the Q_PROPERTY macro. + */ + +/*! + The constructor sets the \a parent and the \a name, but + everything else is set to default values. + */ +PropertyNode::PropertyNode(Aggregate *parent, const QString& name) + : LeafNode(Property, parent, name), + stored_(FlagValueDefault), + designable_(FlagValueDefault), + scriptable_(FlagValueDefault), + writable_(FlagValueDefault), + user_(FlagValueDefault), + const_(false), + final_(false), + revision_(-1), + overrides_(0) +{ + setGenus(Node::CPP); +} + +/*! + Sets this property's \e {overridden from} property to + \a baseProperty, which indicates that this property + overrides \a baseProperty. To begin with, all the values + in this property are set to the corresponding values in + \a baseProperty. + + We probably should ensure that the constant and final + attributes are not being overridden improperly. + */ +void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty) +{ + for (int i = 0; i < NumFunctionRoles; ++i) { + if (functions_[i].isEmpty()) + functions_[i] = baseProperty->functions_[i]; + } + if (stored_ == FlagValueDefault) + stored_ = baseProperty->stored_; + if (designable_ == FlagValueDefault) + designable_ = baseProperty->designable_; + if (scriptable_ == FlagValueDefault) + scriptable_ = baseProperty->scriptable_; + if (writable_ == FlagValueDefault) + writable_ = baseProperty->writable_; + if (user_ == FlagValueDefault) + user_ = baseProperty->user_; + overrides_ = baseProperty; +} + +/*! + */ +QString PropertyNode::qualifiedDataType() const +{ + if (setters().isEmpty() && resetters().isEmpty()) { + if (type_.contains(QLatin1Char('*')) || type_.contains(QLatin1Char('&'))) { + // 'QWidget *' becomes 'QWidget *' const + return type_ + " const"; + } + else { + /* + 'int' becomes 'const int' ('int const' is + correct C++, but looks wrong) + */ + return "const " + type_; + } + } + else { + return type_; + } +} + +bool QmlTypeNode::qmlOnly = false; +QMultiMap<QString,Node*> QmlTypeNode::inheritedBy; + +/*! + Constructs a Qml class node. The new node has the given + \a parent and \a name. + */ +QmlTypeNode::QmlTypeNode(Aggregate *parent, const QString& name) + : Aggregate(QmlType, parent, name), + abstract_(false), + cnodeRequired_(false), + wrapper_(false), + cnode_(0), + logicalModule_(0), + qmlBaseNode_(0) +{ + int i = 0; + if (name.startsWith("QML:")) { + qDebug() << "BOGUS QML qualifier:" << name; + i = 4; + } + setTitle(name.mid(i)); + setPageType(Node::ApiPage); + setGenus(Node::QML); +} + +/*! + Needed for printing a debug messages. + */ +QmlTypeNode::~QmlTypeNode() +{ + // nothing. +} + +/*! + Clear the static maps so that subsequent runs don't try to use + contents from a previous run. + */ +void QmlTypeNode::terminate() +{ + inheritedBy.clear(); +} + +/*! + Record the fact that QML class \a base is inherited by + QML class \a sub. + */ +void QmlTypeNode::addInheritedBy(const QString& base, Node* sub) +{ + if (sub->isInternal()) + return; + if (inheritedBy.constFind(base,sub) == inheritedBy.constEnd()) + inheritedBy.insert(base,sub); +} + +/*! + Loads the list \a subs with the nodes of all the subclasses of \a base. + */ +void QmlTypeNode::subclasses(const QString& base, NodeList& subs) +{ + subs.clear(); + if (inheritedBy.count(base) > 0) { + subs = inheritedBy.values(base); + } +} + +QmlTypeNode* QmlTypeNode::qmlBaseNode() +{ + if (!qmlBaseNode_ && !qmlBaseName_.isEmpty()) { + qmlBaseNode_ = QDocDatabase::qdocDB()->findQmlType(qmlBaseName_); + } + return qmlBaseNode_; +} + +/*! + If this QML type node has a base type node, + return the fully qualified name of that QML + type, i.e. <QML-module-name>::<QML-type-name>. + */ +QString QmlTypeNode::qmlFullBaseName() const +{ + QString result; + if (qmlBaseNode_) { + result = qmlBaseNode_->logicalModuleName() + "::" + qmlBaseNode_->name(); + } + return result; +} + +/*! + If the QML type's QML module pointer is set, return the QML + module name from the QML module node. Otherwise, return the + empty string. + */ +QString QmlTypeNode::logicalModuleName() const +{ + return (logicalModule_ ? logicalModule_->logicalModuleName() : QString()); +} + +/*! + If the QML type's QML module pointer is set, return the QML + module version from the QML module node. Otherwise, return + the empty string. + */ +QString QmlTypeNode::logicalModuleVersion() const +{ + return (logicalModule_ ? logicalModule_->logicalModuleVersion() : QString()); +} + +/*! + If the QML type's QML module pointer is set, return the QML + module identifier from the QML module node. Otherwise, return + the empty string. + */ +QString QmlTypeNode::logicalModuleIdentifier() const +{ + return (logicalModule_ ? logicalModule_->logicalModuleIdentifier() : QString()); +} + +/*! + Constructs a Qml basic type node. The new node has the given + \a parent and \a name. + */ +QmlBasicTypeNode::QmlBasicTypeNode(Aggregate *parent, + const QString& name) + : Aggregate(QmlBasicType, parent, name) +{ + setTitle(name); + setGenus(Node::QML); +} + +/*! + Constructor for the Qml property group node. \a parent is + always a QmlTypeNode. + */ +QmlPropertyGroupNode::QmlPropertyGroupNode(QmlTypeNode* parent, const QString& name) + : Aggregate(QmlPropertyGroup, parent, name) +{ + idNumber_ = -1; + setGenus(Node::QML); +} + +/*! + Return the property group node's id number for use in + constructing an id attribute for the property group. + If the id number is currently -1, increment the global + property group count and set the id number to the new + value. + */ +QString QmlPropertyGroupNode::idNumber() +{ + if (idNumber_ == -1) + idNumber_ = incPropertyGroupCount(); + return QString().setNum(idNumber_); +} + +/*! + Constructor for the QML property node. + */ +QmlPropertyNode::QmlPropertyNode(Aggregate* parent, + const QString& name, + const QString& type, + bool attached) + : LeafNode(QmlProperty, parent, name), + type_(type), + stored_(FlagValueDefault), + designable_(FlagValueDefault), + isAlias_(false), + isdefault_(false), + attached_(attached), + readOnly_(FlagValueDefault) +{ + setPageType(ApiPage); + if (type_ == QString("alias")) + isAlias_ = true; + if (name.startsWith("__")) + setStatus(Internal); + setGenus(Node::QML); +} + +/*! + Returns \c true if a QML property or attached property is + not read-only. The algorithm for figuring this out is long + amd tedious and almost certainly will break. It currently + doesn't work for the qmlproperty: + + \code + bool PropertyChanges::explicit, + \endcode + + ...because the tokenizer gets confused on \e{explicit}. + */ +bool QmlPropertyNode::isWritable() +{ + if (readOnly_ != FlagValueDefault) + return !fromFlagValue(readOnly_, false); + + QmlTypeNode* qcn = qmlTypeNode(); + if (qcn) { + if (qcn->cppClassRequired()) { + if (qcn->classNode()) { + PropertyNode* pn = findCorrespondingCppProperty(); + if (pn) + return pn->isWritable(); + else + defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 " + "in C++ class documented as QML type: " + "(property not found in the C++ class or its base classes)") + .arg(logicalModuleName()).arg(qmlTypeName()).arg(name())); + } + else + defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 " + "in C++ class documented as QML type: " + "(C++ class not specified or not found).") + .arg(logicalModuleName()).arg(qmlTypeName()).arg(name())); + } + } + return true; +} + +/*! + Returns a pointer this QML property's corresponding C++ + property, if it has one. + */ +PropertyNode* QmlPropertyNode::findCorrespondingCppProperty() +{ + PropertyNode* pn; + Node* n = parent(); + while (n && !(n->isQmlType() || n->isJsType())) + n = n->parent(); + if (n) { + QmlTypeNode* qcn = static_cast<QmlTypeNode*>(n); + ClassNode* cn = qcn->classNode(); + if (cn) { + /* + If there is a dot in the property name, first + find the C++ property corresponding to the QML + property group. + */ + QStringList dotSplit = name().split(QChar('.')); + pn = cn->findPropertyNode(dotSplit[0]); + if (pn) { + /* + Now find the C++ property corresponding to + the QML property in the QML property group, + <group>.<property>. + */ + if (dotSplit.size() > 1) { + QStringList path(extractClassName(pn->qualifiedDataType())); + Node* nn = QDocDatabase::qdocDB()->findClassNode(path); + if (nn) { + ClassNode* cn = static_cast<ClassNode*>(nn); + PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]); + /* + If found, return the C++ property + corresponding to the QML property. + Otherwise, return the C++ property + corresponding to the QML property + group. + */ + return (pn2 ? pn2 : pn); + } + } + else + return pn; + } + } + } + return 0; +} + +/*! + This returns the name of the owning QML type. + */ +QString QmlPropertyNode::element() const +{ + if (parent()->isQmlPropertyGroup()) + return parent()->element(); + return parent()->name(); +} + +/*! + Construct the full document name for this node and return it. + */ +QString Node::fullDocumentName() const +{ + QStringList pieces; + const Node* n = this; + + do { + if (!n->name().isEmpty() && !n->isQmlPropertyGroup()) + pieces.insert(0, n->name()); + + if ((n->isQmlType() || n->isJsType()) && !n->logicalModuleName().isEmpty()) { + pieces.insert(0, n->logicalModuleName()); + break; + } + + if (n->isDocumentNode()) + break; + + // Examine the parent node if one exists. + if (n->parent()) + n = n->parent(); + else + break; + } while (true); + + // Create a name based on the type of the ancestor node. + QString concatenator = "::"; + if (n->isQmlType() || n->isJsType()) + concatenator = QLatin1Char('.'); + + if (n->isDocumentNode()) + concatenator = QLatin1Char('#'); + + return pieces.join(concatenator); +} + +/*! + Returns the \a str as an NCName, which means the name can + be used as the value of an \e id attribute. Search for NCName + on the internet for details of what can be an NCName. + */ +QString Node::cleanId(const QString &str) +{ + QString clean; + QString name = str.simplified(); + + if (name.isEmpty()) + return clean; + + name = name.replace("::","-"); + name = name.replace(QLatin1Char(' '), QLatin1Char('-')); + name = name.replace("()","-call"); + + clean.reserve(name.size() + 20); + if (!str.startsWith("id-")) + clean = "id-"; + const QChar c = name[0]; + const uint u = c.unicode(); + + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) { + clean += c; + } + else if (u == '~') { + clean += "dtor."; + } + else if (u == '_') { + clean += "underscore."; + } + else { + clean += QLatin1Char('a'); + } + + for (int i = 1; i < (int) name.length(); i++) { + const QChar c = name[i]; + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9') || u == '-' || + u == '_' || u == '.') { + clean += c; + } + else if (c.isSpace() || u == ':' ) { + clean += QLatin1Char('-'); + } + else if (u == '!') { + clean += "-not"; + } + else if (u == '&') { + clean += "-and"; + } + else if (u == '<') { + clean += "-lt"; + } + else if (u == '=') { + clean += "-eq"; + } + else if (u == '>') { + clean += "-gt"; + } + else if (u == '#') { + clean += "-hash"; + } + else if (u == '(') { + clean += QLatin1Char('-'); + } + else if (u == ')') { + clean += QLatin1Char('-'); + } + else { + clean += QLatin1Char('-'); + clean += QString::number((int)u, 16); + } + } + return clean; +} + +#if 0 +/*! + Creates a string that can be used as a UUID for the node, + depending on the type and subtype of the node. Uniquenss + is not guaranteed, but it is expected that strings created + here will be unique within an XML document. Hence, the + returned string can be used as the value of an \e id + attribute. + */ +QString Node::idForNode() const +{ + const FunctionNode* func; + const TypedefNode* tdn; + QString str; + + switch (type()) { + case Node::Namespace: + str = "namespace-" + fullDocumentName(); + break; + case Node::Class: + str = "class-" + fullDocumentName(); + break; + case Node::Enum: + str = "enum-" + name(); + break; + case Node::Typedef: + tdn = static_cast<const TypedefNode*>(this); + if (tdn->associatedEnum()) { + return tdn->associatedEnum()->idForNode(); + } + else { + str = "typedef-" + name(); + } + break; + case Node::Function: + func = static_cast<const FunctionNode*>(this); + if (func->associatedProperty()) { + return func->associatedProperty()->idForNode(); + } + else { + if (func->name().startsWith("operator")) { + str.clear(); + /* + The test below should probably apply to all + functions, but for now, overloaded operators + are the only ones that produce duplicate id + attributes in the DITA XML files. + */ + if (relatesTo_) + str = "nonmember-"; + QString op = func->name().mid(8); + if (!op.isEmpty()) { + int i = 0; + while (i<op.size() && op.at(i) == ' ') + ++i; + if (i>0 && i<op.size()) { + op = op.mid(i); + } + if (!op.isEmpty()) { + i = 0; + while (i < op.size()) { + const QChar c = op.at(i); + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) + break; + ++i; + } + str += "operator-"; + if (i>0) { + QString tail = op.mid(i); + op = op.left(i); + if (operators_.contains(op)) { + str += operators_.value(op); + if (!tail.isEmpty()) + str += QLatin1Char('-') + tail; + } + else + qDebug() << "qdoc internal error: Operator missing from operators_ map:" << op; + } + else { + str += op; + } + } + } + } + else if (parent_) { + if (parent_->isClass()) + str = "class-member-" + func->name(); + else if (parent_->isNamespace()) + str = "namespace-member-" + func->name(); + else if (parent_->isQmlType()) + str = "qml-method-" + parent_->name().toLower() + "-" + func->name(); + else if (parent_->isJsType()) + str = "js-method-" + parent_->name().toLower() + "-" + func->name(); + else if (parent_->type() == Document) { + qDebug() << "qdoc internal error: Node subtype not handled:" + << parent_->docSubtype() << func->name(); + } + else + qDebug() << "qdoc internal error: Node type not handled:" + << parent_->type() << func->name(); + + } + if (func->overloadNumber() != 0) + str += QLatin1Char('-') + QString::number(func->overloadNumber()); + } + break; + case Node::QmlType: + if (genus() == QML) + str = "qml-class-" + name(); + else + str = "js-type-" + name(); + break; + case Node::QmlBasicType: + if (genus() == QML) + str = "qml-basic-type-" + name(); + else + str = "js-basic-type-" + name(); + break; + case Node::Document: + { + switch (docSubtype()) { + case Node::Page: + case Node::HeaderFile: + str = title(); + if (str.isEmpty()) { + str = name(); + if (str.endsWith(".html")) + str.remove(str.size()-5,5); + } + str.replace(QLatin1Char('/'), QLatin1Char('-')); + break; + case Node::File: + str = name(); + str.replace(QLatin1Char('/'), QLatin1Char('-')); + break; + case Node::Example: + str = name(); + str.replace(QLatin1Char('/'), QLatin1Char('-')); + break; + default: + qDebug() << "ERROR: A case was not handled in Node::idForNode():" + << "docSubtype():" << docSubtype() << "type():" << type(); + break; + } + } + break; + case Node::Group: + case Node::Module: + str = title(); + if (str.isEmpty()) { + str = name(); + if (str.endsWith(".html")) + str.remove(str.size()-5,5); + } + str.replace(QLatin1Char('/'), QLatin1Char('-')); + break; + case Node::QmlModule: + if (genus() == QML) + str = "qml-module-" + name(); + else + str = "js-module-" + name(); + break; + case Node::QmlProperty: + if (genus() == QML) + str = "qml-"; + else + str = "js-"; + if (isAttached()) + str += "attached-property-" + name(); + else + str += "property-" + name(); + break; + case Node::QmlPropertyGroup: + { + Node* n = const_cast<Node*>(this); + if (genus() == QML) + str = "qml-propertygroup-" + n->name(); + else + str = "js-propertygroup-" + n->name(); + } + break; + case Node::Property: + str = "property-" + name(); + break; + case Node::QmlSignal: + if (genus() == QML) + str = "qml-signal-" + name(); + else + str = "js-signal-" + name(); + break; + case Node::QmlSignalHandler: + if (genus() == QML) + str = "qml-signal-handler-" + name(); + else + str = "js-signal-handler-" + name(); + break; + case Node::QmlMethod: + func = static_cast<const FunctionNode*>(this); + if (genus() == QML) + str = "qml-method-"; + else + str = "js-method-"; + str += parent_->name().toLower() + "-" + func->name(); + if (func->overloadNumber() != 0) + str += QLatin1Char('-') + QString::number(func->overloadNumber()); + break; + case Node::Variable: + str = "var-" + name(); + break; + default: + qDebug() << "ERROR: A case was not handled in Node::idForNode():" + << "type():" << type() << "docSubtype():" << docSubtype(); + break; + } + if (str.isEmpty()) { + qDebug() << "ERROR: A link text was empty in Node::idForNode():" + << "type():" << type() << "docSubtype():" << docSubtype() + << "name():" << name() + << "title():" << title(); + } + else { + str = cleanId(str); + } + return str; +} +#endif + +/*! + Prints the inner node's list of children. + For debugging only. + */ +void Aggregate::printChildren(const QString& title) +{ + qDebug() << title << name() << children_.size(); + if (children_.size() > 0) { + for (int i=0; i<children_.size(); ++i) { + Node* n = children_.at(i); + qDebug() << " CHILD:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString(); + } + } +} + +/*! + Returns \c true if the collection node's member list is + not empty. + */ +bool CollectionNode::hasMembers() const +{ + return !members_.isEmpty(); +} + +/*! + Appends \a node to the collection node's member list, if + and only if it isn't already in the member list. + */ +void CollectionNode::addMember(Node* node) +{ + if (!members_.contains(node)) + members_.append(node); +} + +/*! + Returns \c true if this collection node contains at least + one namespace node. + */ +bool CollectionNode::hasNamespaces() const +{ + if (!members_.isEmpty()) { + NodeList::const_iterator i = members_.begin(); + while (i != members_.end()) { + if ((*i)->isNamespace()) + return true; + ++i; + } + } + return false; +} + +/*! + Returns \c true if this collection node contains at least + one class node. + */ +bool CollectionNode::hasClasses() const +{ + if (!members_.isEmpty()) { + NodeList::const_iterator i = members_.cbegin(); + while (i != members_.cend()) { + if ((*i)->isClass()) + return true; + ++i; + } + } + return false; +} + +/*! + Loads \a out with all this collection node's members that + are namespace nodes. + */ +void CollectionNode::getMemberNamespaces(NodeMap& out) +{ + out.clear(); + NodeList::const_iterator i = members_.cbegin(); + while (i != members_.cend()) { + if ((*i)->isNamespace()) + out.insert((*i)->name(),(*i)); + ++i; + } +} + +/*! + Loads \a out with all this collection node's members that + are class nodes. + */ +void CollectionNode::getMemberClasses(NodeMap& out) const +{ + out.clear(); + NodeList::const_iterator i = members_.cbegin(); + while (i != members_.cend()) { + if ((*i)->isClass()) + out.insert((*i)->name(),(*i)); + ++i; + } +} + +/*! + Prints the collection node's list of members. + For debugging only. + */ +void CollectionNode::printMembers(const QString& title) +{ + qDebug() << title << name() << members_.size(); + if (members_.size() > 0) { + for (int i=0; i<members_.size(); ++i) { + Node* n = members_.at(i); + qDebug() << " MEMBER:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString(); + } + } +} + +/*! + Sets the document node's \a title. This is used for the page title. + */ +void CollectionNode::setTitle(const QString& title) +{ + title_ = title; + parent()->addChild(this, title); +} + +/*! + This function splits \a arg on the blank character to get a + logical module name and version number. If the version number + is present, it spilts the version number on the '.' character + to get a major version number and a minor vrsion number. If + the version number is present, both the major and minor version + numbers should be there, but the minor version number is not + absolutely necessary. + */ +void CollectionNode::setLogicalModuleInfo(const QString& arg) +{ + QStringList blankSplit = arg.split(QLatin1Char(' ')); + logicalModuleName_ = blankSplit[0]; + if (blankSplit.size() > 1) { + QStringList dotSplit = blankSplit[1].split(QLatin1Char('.')); + logicalModuleVersionMajor_ = dotSplit[0]; + if (dotSplit.size() > 1) + logicalModuleVersionMinor_ = dotSplit[1]; + else + logicalModuleVersionMinor_ = "0"; + } +} + +/*! + This function accepts the logical module \a info as a string + list. If the logical module info contains the version number, + it spilts the version number on the '.' character to get the + major and minor vrsion numbers. Both major and minor version + numbers should be provided, but the minor version number is + not strictly necessary. + */ +void CollectionNode::setLogicalModuleInfo(const QStringList& info) +{ + logicalModuleName_ = info[0]; + if (info.size() > 1) { + QStringList dotSplit = info[1].split(QLatin1Char('.')); + logicalModuleVersionMajor_ = dotSplit[0]; + if (dotSplit.size() > 1) + logicalModuleVersionMinor_ = dotSplit[1]; + else + logicalModuleVersionMinor_ = "0"; + } +} + +QT_END_NAMESPACE |