summaryrefslogtreecommitdiff
path: root/src/qdoc/node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/node.cpp')
-rw-r--r--src/qdoc/node.cpp3038
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 &parameter, 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