/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "node.h" #include "tree.h" #include "codemarker.h" #include "codeparser.h" #include #include "qdocdatabase.h" #include QT_BEGIN_NAMESPACE int Node::propertyGroupCount_ = 0; QStringMap Node::operators_; QMap 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::Type. The map value is the enum value. There should be an entry in the map for each value in the Type 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_) 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()->subType() == Node::Collision || node->parent()->name().isEmpty()) break; fullName.prepend(QLatin1String("::")); node = node->parent(); } return fullName; } /*! Constructs and returns this node's full name. The \a relative node is either null or is a collision node. */ QString Node::fullName(const Node* relative) const { if (isDocNode() || isCollectionNode()) { const DocNode* dn = static_cast(this); // Only print modulename::type on collision pages. if (!dn->qmlModuleName().isEmpty() && relative != 0 && relative->isCollisionNode()) return dn->qmlModuleName() + "::" + dn->title(); return dn->title(); } else if (isClass()) { const ClassNode* cn = static_cast(this); if (!cn->serviceName().isEmpty()) return cn->serviceName(); } 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::Type, and the second value is a Node::SubType. 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; iaddChild(this); outSubDir_ = CodeParser::currentOutputSubdirectory(); 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_); } /*! Returns the page type \a t as a string, for use as an attribute value in XML or HTML. */ QString Node::pageTypeString(unsigned 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 t) { switch ((Type)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(subType()); } /*! 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 t) { switch ((SubType)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 Collision: return "collision"; 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_ = ApiPage; else if (t == "howto") pageType_ = HowToPage; else if (t == "overview") pageType_ = OverviewPage; else if (t == "tutorial") pageType_ = TutorialPage; else if (t == "howto") pageType_ = HowToPage; else if (t == "article") pageType_ = ArticlePage; else if (t == "example") pageType_ = ExamplePage; else if (t == "ditamap") pageType_ = 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(InnerNode *pseudoParent) { if (relatesTo_) { relatesTo_->removeRelated(this); } relatesTo_ = pseudoParent; pseudoParent->related_.append(this); } /*! 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 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_) { 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 = Commendable; 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_ && safeness_ == parent_->inheritedThreadSafeness()) return UnspecifiedSafeness; return 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_ && safeness_ == UnspecifiedSafeness) return parent_->inheritedThreadSafeness(); return 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(); } #endif /*! 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_; } /*! If this node is a QML class node, return a pointer to it. If it is a child of a QML class node, return a pointer to the QML class node. Otherwise, return 0; */ QmlClassNode* Node::qmlClassNode() { if (isQmlNode()) { Node* n = this; while (n && !n->isQmlType()) n = n->parent(); if (n && n->isQmlType()) return static_cast(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() { QmlClassNode* qcn = qmlClassNode(); 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); } /*! \class InnerNode */ /*! The inner node destructor deletes the children and removes this node from its related nodes. */ InnerNode::~InnerNode() { deleteChildren(); removeFromRelated(); } /*! Find the node in this node's children that has the given \a name. If this node is a QML class node, be sure to also look in the children of its property group nodes. Return the matching node or 0. */ Node *InnerNode::findChildNode(const QString& name) const { Node *node = childMap.value(name); if (node && !node->isQmlPropertyGroup()) return node; if (isQmlType()) { for (int i=0; iisQmlPropertyGroup()) { node = static_cast(n)->findChildNode(name); if (node) return node; } } } return primaryFunctionMap.value(name); } /*! Find the node in this node's children that has the given \a name. If this node is a QML class node, be sure to also look in the children of its property group nodes. Return the matching node or 0. This is not a recearsive search. If \a qml is true, only match a node for which node->isQmlNode() returns \c true. If \a qml is false, only match a node for which node->isQmlNode() returns \c false. */ Node* InnerNode::findChildNode(const QString& name, bool qml) const { QList nodes = childMap.values(name); if (!nodes.isEmpty()) { for (int i=0; iisQmlNode()) return node; } else if (node->isQmlNode()) return node; } } if (qml && isQmlType()) { for (int i=0; iisQmlPropertyGroup()) { node = static_cast(node)->findChildNode(name); if (node) return node; } } } return primaryFunctionMap.value(name); } /*! 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. This function is not recursive and therefore can't handle collisions. If it finds a collision node named \a name, it will return that node. But it might not find the collision node because it looks up \a name in the child map, not the list. */ Node* InnerNode::findChildNode(const QString& name, Type type) { if (type == Function) return primaryFunctionMap.value(name); else { QList nodes = childMap.values(name); for (int i=0; itype() == type) return node; } } return 0; } /*! */ void InnerNode::findNodes(const QString& name, QList& n) { n.clear(); Node* node = 0; QList nodes = childMap.values(name); /* If this node's child map contains no nodes named name, then if this node is a QML class, search each of its property group nodes for a node named name. If a match is found, append it to the output list and return immediately. */ if (nodes.isEmpty()) { if (isQmlType()) { for (int i=0; iisQmlPropertyGroup()) { node = static_cast(node)->findChildNode(name); if (node) { n.append(node); return; } } } } } else { /* If the childMap does contain one or more nodes named name, traverse the list of matching nodes. Append each matching node that is not a property group node to the output list. Search each property group node for a node named name and append that node to the output list. This is overkill, I think, but should produce a useful list. */ for (int i=0; iisQmlPropertyGroup()) n.append(node); else { node = static_cast(node)->findChildNode(name); if (node) n.append(node); } } } if (!n.isEmpty()) return; node = primaryFunctionMap.value(name); if (node) n.append(node); } /*! Find a function node that is a child of this nose, such that the function node has the specified \a name. */ FunctionNode *InnerNode::findFunctionNode(const QString& name) const { return static_cast(primaryFunctionMap.value(name)); } /*! 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 *InnerNode::findFunctionNode(const FunctionNode *clone) { QMap::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 InnerNode::primaryKeys() { QStringList t; QMap::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 InnerNode::secondaryKeys() { QStringList t; QMap::iterator i = secondaryFunctionMap.begin(); while (i != secondaryFunctionMap.end()) { t.append(i.key()); ++i; } return t; } /*! */ void InnerNode::setOverload(const FunctionNode *func, bool overlode) { Node *node = (Node *) func; Node *&primary = primaryFunctionMap[func->name()]; if (secondaryFunctionMap.contains(func->name())) { NodeList& secs = secondaryFunctionMap[func->name()]; if (overlode) { if (primary == node) { primary = secs.first(); secs.erase(secs.begin()); secs.append(node); } else { secs.removeAll(node); secs.append(node); } } else { if (primary != node) { secs.removeAll(node); secs.prepend(primary); primary = node; } } } } /*! Mark all child nodes that have no documentation as having private access and internal status. qdoc will then ignore them for documentation purposes. \note Exception: Name collision nodes are not marked private/internal. */ void InnerNode::makeUndocumentedChildrenInternal() { foreach (Node *child, childNodes()) { if (child->doc().isEmpty()) { if (child->subType() != Node::Collision) { child->setAccess(Node::Private); child->setStatus(Node::Internal); } } } } /*! */ void InnerNode::normalizeOverloads() { QMap::Iterator p1 = primaryFunctionMap.begin(); while (p1 != primaryFunctionMap.end()) { FunctionNode *primaryFunc = (FunctionNode *) *p1; if (secondaryFunctionMap.contains(primaryFunc->name()) && (primaryFunc->status() != Commendable || primaryFunc->access() == Private)) { NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; NodeList::ConstIterator s = secs.constBegin(); while (s != secs.constEnd()) { FunctionNode *secondaryFunc = (FunctionNode *) *s; // Any non-obsolete, non-compatibility, non-private functions // (i.e, visible functions) are preferable to the primary // function. if (secondaryFunc->status() == Commendable && secondaryFunc->access() != Private) { *p1 = secondaryFunc; int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc); secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc); break; } ++s; } } ++p1; } QMap::ConstIterator p = primaryFunctionMap.constBegin(); while (p != primaryFunctionMap.constEnd()) { FunctionNode *primaryFunc = (FunctionNode *) *p; if (primaryFunc->isOverload()) primaryFunc->ove = false; if (secondaryFunctionMap.contains(primaryFunc->name())) { NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; NodeList::ConstIterator s = secs.constBegin(); while (s != secs.constEnd()) { FunctionNode *secondaryFunc = (FunctionNode *) *s; if (!secondaryFunc->isOverload()) secondaryFunc->ove = true; ++s; } } ++p; } NodeList::ConstIterator c = childNodes().constBegin(); while (c != childNodes().constEnd()) { if ((*c)->isInnerNode()) ((InnerNode *) *c)->normalizeOverloads(); ++c; } } /*! */ void InnerNode::removeFromRelated() { while (!related_.isEmpty()) { Node *p = static_cast(related_.takeFirst()); if (p != 0 && p->relates() == this) p->clearRelated(); } } /*! Deletes all this node's children. */ void InnerNode::deleteChildren() { NodeList childrenCopy = children_; // `children_` will be changed in ~Node() qDeleteAll(childrenCopy); } /*! \fn bool InnerNode::isInnerNode() 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 *InnerNode::findEnumNodeForValue(const QString &enumValue) const { foreach (const Node *node, enumChildren_) { const EnumNode *en = static_cast(node); if (en->hasItem(enumValue)) return en; } return 0; } /*! Returnds the sequence number of the function node \a func in the list of overloaded functions for a class, such that all the functions have the same name as the \a func. */ int InnerNode::overloadNumber(const FunctionNode *func) const { Node *node = (Node *) func; if (primaryFunctionMap[func->name()] == node) { return 1; } else { return secondaryFunctionMap[func->name()].indexOf(node) + 2; } } /*! Returns a node list containing all the member functions of some class such that the functions overload the name \a funcName. */ NodeList InnerNode::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. */ InnerNode::InnerNode(Type type, InnerNode *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 InnerNode::addInclude(const QString& include) { includes_.append(include); } /*! Sets the list of include files to \a includes. */ void InnerNode::setIncludes(const QStringList& includes) { includes_ = includes; } /*! f1 is always the clone */ bool InnerNode::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) { if (f1->parameters().count() != f2->parameters().count()) return false; if (f1->isConst() != f2->isConst()) return false; QList::ConstIterator p1 = f1->parameters().constBegin(); QList::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->leftType(); QString t2 = p2->leftType(); 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. */ void InnerNode::addChild(Node *child) { children_.append(child); if ((child->type() == Function) || (child->type() == QmlMethod)) { FunctionNode *func = (FunctionNode *) child; if (!primaryFunctionMap.contains(func->name())) { primaryFunctionMap.insert(func->name(), func); } else { NodeList &secs = secondaryFunctionMap[func->name()]; secs.append(func); } } else { if (child->type() == Enum) enumChildren_.append(child); childMap.insertMulti(child->name(), child); } } /*! 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 InnerNode::addChild(Node* child, const QString& title) { childMap.insertMulti(title, child); } /*! */ void InnerNode::removeChild(Node *child) { children_.removeAll(child); enumChildren_.removeAll(child); if (child->type() == Function) { QMap::Iterator prim = primaryFunctionMap.find(child->name()); NodeList& secs = secondaryFunctionMap[child->name()]; if (prim != primaryFunctionMap.end() && *prim == child) { if (secs.isEmpty()) { primaryFunctionMap.remove(child->name()); } else { primaryFunctionMap.insert(child->name(), secs.takeFirst()); } } else { secs.removeAll(child); } } QMap::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; } } /*! 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::moduleName() const { if (!moduleName_.isEmpty()) return moduleName_; 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 moduleName = moduleDir.left(finish); if (moduleName == "corelib") return "QtCore"; else if (moduleName == "uitools") return "QtUiTools"; else if (moduleName == "gui") return "QtGui"; else if (moduleName == "network") return "QtNetwork"; else if (moduleName == "opengl") return "QtOpenGL"; else if (moduleName == "svg") return "QtSvg"; else if (moduleName == "sql") return "QtSql"; else if (moduleName == "qtestlib") return "QtTest"; else if (moduleDir.contains("webkit")) return "QtWebKit"; else if (moduleName == "xml") return "QtXml"; else return QString(); } /*! */ void InnerNode::removeRelated(Node *pseudoChild) { related_.removeAll(pseudoChild); } /*! If this node has a child that is a QML property named \a n, return the pointer to that child. */ QmlPropertyNode* InnerNode::hasQmlProperty(const QString& n) const { foreach (Node* child, childNodes()) { if (child->type() == Node::QmlProperty) { if (child->name() == n) return static_cast(child); } else if (child->isQmlPropertyGroup()) { QmlPropertyNode* t = child->hasQmlProperty(n); if (t) return t; } } return 0; } /*! \class LeafNode */ /*! \fn bool LeafNode::isInnerNode() 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(Type type, InnerNode *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(InnerNode* parent, Type 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(InnerNode *parent, const QString& name) : InnerNode(Namespace, parent, name), tree_(0) { 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(InnerNode *parent, const QString& name) : InnerNode(Class, parent, name) { abstract_ = false; wrapper_ = false; qmlelement = 0; 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)); } /*! */ void ClassNode::fixBaseClasses() { int i; i = 0; QSet 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 &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 &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::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(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(n); PropertyNode* pn = 0; const QList &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& 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. */ QmlClassNode* ClassNode::findQmlBaseNode() { QmlClassNode* result = 0; const QList& 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 DocNode */ /*! The type of a DocNode is Document, and it has a \a subtype, which specifies the type of DocNode. The page type for the page index is set here. */ DocNode::DocNode(InnerNode* parent, const QString& name, SubType subtype, Node::PageType ptype) : InnerNode(Document, parent, name), nodeSubtype_(subtype) { switch (subtype) { case Page: setPageType(ptype); break; case DitaMap: setPageType(ptype); break; case Example: setPageType(ExamplePage); break; case Collision: setPageType(ptype); break; default: break; } } /*! \fn QString DocNode::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 DocNode::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 SubType values is different from title() */ QString DocNode::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 if (nodeSubtype_ == Collision) { return title(); } else { return title(); } } /*! Returns the subtitle. */ QString DocNode::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(InnerNode *parent, const QString& name) : LeafNode(Enum, parent, name), ft(0) { // nothing. } /*! Add \a item to the enum type's item list. */ void EnumNode::addItem(const EnumItem& item) { itms.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, itms) { if (item.name() == name) return item.value(); } return QString(); } /*! \class TypedefNode */ /*! */ TypedefNode::TypedefNode(InnerNode *parent, const QString& name) : LeafNode(Typedef, parent, name), ae(0) { } /*! */ void TypedefNode::setAssociatedEnum(const EnumNode *enume) { ae = 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 leftType 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& leftType, const QString& rightType, const QString& name, const QString& defaultValue) : lef(leftType), rig(rightType), nam(name), def(defaultValue) { } /*! The standard copy constructor copies the strings from \a p. */ Parameter::Parameter(const Parameter& p) : lef(p.lef), rig(p.rig), nam(p.nam), def(p.def) { } /*! Assigning Parameter \a p to this Parameter copies the strings across. */ Parameter& Parameter::operator=(const Parameter& p) { lef = p.lef; rig = p.rig; nam = p.nam; def = p.def; 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 = lef + rig; if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' '))) p += QLatin1Char(' '); p += nam; if (value && !def.isEmpty()) p += " = " + def; 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. */ FunctionNode::FunctionNode(InnerNode *parent, const QString& name) : LeafNode(Function, parent, name), met(Plain), vir(NonVirtual), con(false), sta(false), ove(false), reimp(false), attached_(false), rf(0), ap(0) { // nothing. } /*! 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. */ FunctionNode::FunctionNode(Type type, InnerNode *parent, const QString& name, bool attached) : LeafNode(type, parent, name), met(Plain), vir(NonVirtual), con(false), sta(false), ove(false), reimp(false), attached_(attached), rf(0), ap(0) { if (type == QmlMethod || type == QmlSignal) { if (name.startsWith("__")) setStatus(Internal); } } /*! 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 virtualness) { vir = virtualness; if ((virtualness == PureVirtual) && parent() && (parent()->type() == Node::Class)) parent()->setAbstract(true); } /*! */ void FunctionNode::setOverload(bool overlode) { parent()->setOverload(this, overlode); ove = overlode; } /*! Sets the function node's reimplementation flag to \a r. When \a r 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::setReimp(bool r) { reimp = r; } /*! */ void FunctionNode::addParameter(const Parameter& parameter) { params.append(parameter); } /*! */ void FunctionNode::borrowParameterNames(const FunctionNode *source) { QList::Iterator t = params.begin(); QList::ConstIterator s = source->params.constBegin(); while (s != source->params.constEnd() && t != params.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 *from) { rf = from; from->rb.append(this); } /*! Sets the "associated" property to \a property. The function might be the setter or getter for a property, for example. */ void FunctionNode::setAssociatedProperty(PropertyNode *property) { ap = property; } /*! Returns the overload number for this function obtained from the parent. */ int FunctionNode::overloadNumber() const { return parent()->overloadNumber(this); } /*! Returns the list of parameter names. */ QStringList FunctionNode::parameterNames() const { QStringList names; QList::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.leftType() + 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::reconstructParams(bool values) const { QStringList params; QList::ConstIterator p = parameters().constBegin(); while (p != parameters().constEnd()) { params << (*p).reconstruct(values); ++p; } return params; } /*! 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 params = reconstructParams(values); int p = params.size(); if (p > 0) { for (int i=0; ifuncs[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 QmlClassNode::qmlOnly = false; QMultiMap QmlClassNode::inheritedBy; /*! Constructs a Qml class node. The new node has the given \a parent and \a name. */ QmlClassNode::QmlClassNode(InnerNode *parent, const QString& name) : InnerNode(QmlType, parent, name), abstract_(false), cnodeRequired_(false), wrapper_(false), cnode_(0), qmlModule_(0), qmlBaseNode_(0) { int i = 0; if (name.startsWith("QML:")) { qDebug() << "BOGUS QML qualifier:" << name; i = 4; } setTitle(name.mid(i)); setPageType(Node::ApiPage); } /*! Needed for printing a debug messages. */ QmlClassNode::~QmlClassNode() { // nothing. } /*! Clear the static maps so that subsequent runs don't try to use contents from a previous run. */ void QmlClassNode::terminate() { inheritedBy.clear(); } /*! Record the fact that QML class \a base is inherited by QML class \a sub. */ void QmlClassNode::addInheritedBy(const QString& base, Node* sub) { 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 QmlClassNode::subclasses(const QString& base, NodeList& subs) { subs.clear(); if (inheritedBy.count(base) > 0) { subs = inheritedBy.values(base); } } /*! This function splits \a arg on the blank character to get a QML 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 QmlModuleNode::setQmlModuleInfo(const QString& arg) { QStringList blankSplit = arg.split(QLatin1Char(' ')); qmlModuleName_ = blankSplit[0]; if (blankSplit.size() > 1) { QStringList dotSplit = blankSplit[1].split(QLatin1Char('.')); qmlModuleVersionMajor_ = dotSplit[0]; if (dotSplit.size() > 1) qmlModuleVersionMinor_ = dotSplit[1]; else qmlModuleVersionMinor_ = "0"; } } QmlClassNode* QmlClassNode::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. ::. */ QString QmlClassNode::qmlFullBaseName() const { QString result; if (qmlBaseNode_) { result = qmlBaseNode_->qmlModuleName() + "::" + 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 QmlClassNode::qmlModuleName() const { return (qmlModule_ ? qmlModule_->qmlModuleName() : 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 QmlClassNode::qmlModuleVersion() const { return (qmlModule_ ? qmlModule_->qmlModuleVersion() : 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 QmlClassNode::qmlModuleIdentifier() const { return (qmlModule_ ? qmlModule_->qmlModuleIdentifier() : QString()); } /*! Constructs a Qml basic type node. The new node has the given \a parent and \a name. */ QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent, const QString& name) : InnerNode(QmlBasicType, parent, name) { setTitle(name); } /*! Constructor for the Qml property group node. \a parent is always a QmlClassNode. */ QmlPropertyGroupNode::QmlPropertyGroupNode(QmlClassNode* parent, const QString& name) : InnerNode(QmlPropertyGroup, parent, name) { idNumber_ = -1; } /*! 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(InnerNode* 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); } /*! 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); QmlClassNode* qcn = qmlClassNode(); if (qcn) { if (qcn->cppClassRequired()) { if (qcn->classNode()) { PropertyNode* pn = findCorrespondingCppProperty(); if (pn) return pn->isWritable(); else location().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(qmlModuleName()).arg(qmlTypeName()).arg(name())); } else location().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(qmlModuleName()).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 = n->parent(); if (n) { QmlClassNode* qcn = static_cast(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, .. */ if (dotSplit.size() > 1) { QStringList path(extractClassName(pn->qualifiedDataType())); Node* nn = QDocDatabase::qdocDB()->findClassNode(path); if (nn) { ClassNode* cn = static_cast(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(); } /*! \class NameCollisionNode An instance of this node is inserted in the tree whenever qdoc discovers that two nodes have the same name. */ /*! Constructs a name collision node containing \a child as its first child. The parent of \a child becomes this node's parent. */ NameCollisionNode::NameCollisionNode(InnerNode* child) : DocNode(child->parent(), child->name(), Collision, Node::NoPageType) { setTitle("Name Collision: " + child->name()); addCollision(child); } /*! Add a collision to this collision node. \a child has the same name as the other children in this collision node. \a child becomes the current child. */ void NameCollisionNode::addCollision(InnerNode* child) { if (child) { if (child->parent()) child->parent()->removeChild(child); child->setParent((InnerNode*)this); children_.append(child); } } /*! The destructor does nothing. */ NameCollisionNode::~NameCollisionNode() { // nothing. } /*! Returns \c true if this collision node's current node is a QML node. */ bool NameCollisionNode::isQmlNode() const { return false; } /*! Find any of this collision node's children that has type \a t and subtype \a st and return a pointer to it. */ InnerNode* NameCollisionNode::findAny(Node::Type t, Node::SubType st) { const NodeList& cn = childNodes(); NodeList::ConstIterator i = cn.constBegin(); while (i != cn.constEnd()) { if ((*i)->type() == t && (*i)->subType() == st) return static_cast(*i); ++i; } return 0; } /*! This node is a name collision node. Find a child of this node such that the child's QML module name matches origin's QML module Name. Return the matching node, or return this node if there is no matching node. */ const Node* NameCollisionNode::applyModuleName(const Node* origin) const { if (origin && !origin->qmlModuleName().isEmpty()) { const NodeList& cn = childNodes(); NodeList::ConstIterator i = cn.constBegin(); while (i != cn.constEnd()) { if ((*i)->isQmlType()) { if (origin->qmlModuleName() == (*i)->qmlModuleName()) return (*i); } ++i; } } return this; } /*! First, find all the colliding nodes that have the correct type \a t and subtype \a st. If there is only one node having the correct type and subtype, return that one. If there is more than one node having the correct type and subtype, then, in that subset, if there is only one non-index node, return that one. If there are multiple non-index nodes, return this collision node because we can't disambiguate. Otherwise, if there are multiple nodes having the correct type and subtype, return this collision node because, again, we can't disambiguate. But if there are no nodes at all that have the correct type and subtype, return 0. */ Node* NameCollisionNode::disambiguate(Type t, SubType st) { NodeList nl; const NodeList& cn = childNodes(); NodeList::ConstIterator i = cn.constBegin(); while (i != cn.constEnd()) { if ((*i)->type() == t) { if ((st == NoSubType) || ((*i)->subType() == st)) nl.append((*i)); } ++i; } Node* n = 0; if (!nl.isEmpty()) { i = nl.constBegin(); if (nl.size() == 1) return (*i); while (i != nl.constEnd()) { if (!(*i)->isIndexNode()) { if (n) return this; n = (*i); } ++i; } } return n; } /*! 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->qmlModuleName().isEmpty()) { pieces.insert(0, n->qmlModuleName()); break; } if (n->isDocNode()) 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->isDocNode()) 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(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; } /*! 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(this); if (tdn->associatedEnum()) { return tdn->associatedEnum()->idForNode(); } else { str = "typedef-" + name(); } break; case Node::Function: func = static_cast(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 (i0 && i= '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_->type() == Document) { qDebug() << "qdoc internal error: Node subtype not handled:" << parent_->subType() << func->name(); } else qDebug() << "qdoc internal error: Node type not handled:" << parent_->type() << func->name(); } if (func->overloadNumber() != 1) str += QLatin1Char('-') + QString::number(func->overloadNumber()); } break; case Node::QmlType: str = "qml-class-" + name(); break; case Node::QmlBasicType: str = "qml-basic-type-" + name(); break; case Node::Document: { switch (subType()) { 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; case Node::Collision: str = title(); str.replace(": ","-"); break; default: qDebug() << "ERROR: A case was not handled in Node::idForNode():" << "subType():" << subType() << "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: str = "qml-module-" + name(); break; case Node::QmlProperty: str = "qml-property-" + name(); break; case Node::QmlPropertyGroup: { Node* n = const_cast(this); str = "qml-propertygroup-" + n->name(); } break; case Node::Property: str = "property-" + name(); break; case Node::QmlSignal: str = "qml-signal-" + name(); break; case Node::QmlSignalHandler: str = "qml-signal-handler-" + name(); break; case Node::QmlMethod: func = static_cast(this); str = "qml-method-" + parent_->name().toLower() + "-" + func->name(); if (func->overloadNumber() != 1) 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() << "subType():" << subType(); break; } if (str.isEmpty()) { qDebug() << "ERROR: A link text was empty in Node::idForNode():" << "type():" << type() << "subType():" << subType() << "name():" << name() << "title():" << title(); } else { str = cleanId(str); } return str; } /*! Prints the inner node's list of children. For debugging only. */ void InnerNode::printChildren(const QString& title) { qDebug() << title << name() << children_.size(); if (children_.size() > 0) { for (int i=0; iname() << 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_.begin(); while (i != members_.end()) { 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_.begin(); while (i != members_.end()) { 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) { out.clear(); NodeList::const_iterator i = members_.begin(); while (i != members_.end()) { 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; iname() << 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); } QT_END_NAMESPACE