2834 lines
77 KiB
C++
2834 lines
77 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/
|
|
**
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** GNU Lesser General Public License Usage
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU Lesser
|
|
** General Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License version 3.0 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU General
|
|
** Public License version 3.0 requirements will be met:
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "node.h"
|
|
#include "tree.h"
|
|
#include "codemarker.h"
|
|
#include "codeparser.h"
|
|
#include <QUuid>
|
|
#include <qdebug.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
int Node::propertyGroupCount_ = 0;
|
|
ExampleNodeMap ExampleNode::exampleNodeMap;
|
|
QStringMap Node::operators_;
|
|
|
|
/*!
|
|
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);
|
|
}
|
|
|
|
/*!
|
|
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 (!d.isEmpty() && !replace) {
|
|
doc.location().warning(tr("Overrides a previous doc"));
|
|
d.location().warning(tr("(The previous doc is here)"));
|
|
}
|
|
d = 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(Type type, InnerNode *parent, const QString& name)
|
|
: nodeType_(type),
|
|
access_(Public),
|
|
safeness_(UnspecifiedSafeness),
|
|
pageType_(NoPageType),
|
|
status_(Commendable),
|
|
indexNodeFlag_(false),
|
|
parent_(parent),
|
|
relatesTo_(0),
|
|
name_(name)
|
|
{
|
|
if (parent_)
|
|
parent_->addChild(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");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Returns the node's URL.
|
|
*/
|
|
QString Node::url() const
|
|
{
|
|
return url_;
|
|
}
|
|
|
|
/*!
|
|
Sets the node's URL to \a url
|
|
*/
|
|
void Node::setUrl(const QString &url)
|
|
{
|
|
url_ = 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 Fake:
|
|
return "fake";
|
|
case Enum:
|
|
return "enum";
|
|
case Typedef:
|
|
return "typedef";
|
|
case Function:
|
|
return "function";
|
|
case Property:
|
|
return "property";
|
|
case Variable:
|
|
return "variable";
|
|
case QmlProperty:
|
|
return "QML property";
|
|
case QmlSignal:
|
|
return "QML signal";
|
|
case QmlSignalHandler:
|
|
return "QML signal handler";
|
|
case QmlMethod:
|
|
return "QML method";
|
|
default:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*!
|
|
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 Fake.
|
|
*/
|
|
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 Fake.
|
|
*/
|
|
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 Group:
|
|
return "group";
|
|
case Module:
|
|
return "module";
|
|
case Page:
|
|
return "page";
|
|
case ExternalPage:
|
|
return "external page";
|
|
case QmlClass:
|
|
return "QML class";
|
|
case QmlPropertyGroup:
|
|
return "QML property group";
|
|
case QmlBasicType:
|
|
return "QML basic type";
|
|
case QmlModule:
|
|
return "QML module";
|
|
case DitaMap:
|
|
return "ditamap";
|
|
case Collision:
|
|
return "collision";
|
|
case NoSubType:
|
|
default:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*!
|
|
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<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)
|
|
{
|
|
sinc = 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_;
|
|
}
|
|
|
|
/*!
|
|
Returns the sanitized file name without the path.
|
|
If the 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;
|
|
}
|
|
|
|
#if 0
|
|
// fossil
|
|
QUuid quuid = QUuid::createUuid();
|
|
QString t = quuid.toString();
|
|
uuid = "id-" + t.mid(1,t.length()-2);
|
|
#endif
|
|
|
|
/*!
|
|
Composes a string to be used as an href attribute in DITA
|
|
XML. It is composed of the file name and the UUID separated
|
|
by a '#'. If this node is a class node, the file name is
|
|
taken from this node; if this node is a function node, the
|
|
file name is taken from the parent node of this node.
|
|
*/
|
|
QString Node::ditaXmlHref()
|
|
{
|
|
QString href;
|
|
if ((type() == Function) ||
|
|
(type() == Property) ||
|
|
(type() == Variable)) {
|
|
href = parent()->fileBase();
|
|
}
|
|
else {
|
|
href = fileBase();
|
|
}
|
|
if (!href.endsWith(".xml") && !href.endsWith(".dita"))
|
|
href += ".dita";
|
|
return href + QLatin1Char('#') + guid();
|
|
}
|
|
|
|
/*!
|
|
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->subType() != Node::QmlClass)
|
|
n = n->parent();
|
|
if (n && n->subType() == Node::QmlClass)
|
|
return static_cast<QmlClassNode*>(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 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;
|
|
}
|
|
|
|
/*!
|
|
\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::findChildNodeByName(const QString& name)
|
|
{
|
|
Node *node = childMap.value(name);
|
|
if (node && node->subType() != QmlPropertyGroup)
|
|
return node;
|
|
if ((type() == Fake) && (subType() == QmlClass)) {
|
|
for (int i=0; i<children.size(); ++i) {
|
|
Node* n = children.at(i);
|
|
if (n->subType() == QmlPropertyGroup) {
|
|
node = static_cast<InnerNode*>(n)->findChildNodeByName(name);
|
|
if (node)
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return primaryFunctionMap.value(name);
|
|
}
|
|
void InnerNode::findNodes(const QString& name, QList<Node*>& n)
|
|
{
|
|
n.clear();
|
|
Node* node = 0;
|
|
QList<Node*> nodes = childMap.values(name);
|
|
/*
|
|
<sigh> If this node's child map contains no nodes named
|
|
name, then if this node is a QML class, seach 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 ((type() == Fake) && (subType() == QmlClass)) {
|
|
for (int i=0; i<children.size(); ++i) {
|
|
node = children.at(i);
|
|
if (node->subType() == QmlPropertyGroup) {
|
|
node = static_cast<InnerNode*>(node)->findChildNodeByName(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; i<nodes.size(); ++i) {
|
|
node = nodes.at(i);
|
|
if (node->subType() != QmlPropertyGroup)
|
|
n.append(node);
|
|
else {
|
|
node = static_cast<InnerNode*>(node)->findChildNodeByName(name);
|
|
if (node)
|
|
n.append(node);
|
|
}
|
|
}
|
|
}
|
|
if (!n.isEmpty())
|
|
return;
|
|
node = primaryFunctionMap.value(name);
|
|
if (node)
|
|
n.append(node);
|
|
}
|
|
|
|
/*!
|
|
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 true. If \a qml is false, only match a node for which
|
|
node->isQmlNode() returns false.
|
|
*/
|
|
Node* InnerNode::findChildNodeByName(const QString& name, bool qml)
|
|
{
|
|
QList<Node*> nodes = childMap.values(name);
|
|
if (!nodes.isEmpty()) {
|
|
for (int i=0; i<nodes.size(); ++i) {
|
|
Node* node = nodes.at(i);
|
|
if (!qml) {
|
|
if (!node->isQmlNode())
|
|
return node;
|
|
}
|
|
else if (node->isQmlNode() && (node->subType() != QmlPropertyGroup))
|
|
return node;
|
|
}
|
|
}
|
|
if (qml && (type() == Fake) && (subType() == QmlClass)) {
|
|
for (int i=0; i<children.size(); ++i) {
|
|
Node* node = children.at(i);
|
|
if (node->subType() == QmlPropertyGroup) {
|
|
node = static_cast<InnerNode*>(node)->findChildNodeByName(name);
|
|
if (node)
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return primaryFunctionMap.value(name);
|
|
}
|
|
|
|
/*!
|
|
This function is like findChildNodeByName(), 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::findChildNodeByNameAndType(const QString& name, Type type)
|
|
{
|
|
if (type == Function)
|
|
return primaryFunctionMap.value(name);
|
|
else {
|
|
Node *node = childMap.value(name);
|
|
if (node && 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 *InnerNode::findFunctionNode(const QString& name)
|
|
{
|
|
return static_cast<FunctionNode *>(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<QString,Node*>::ConstIterator c = primaryFunctionMap.find(clone->name());
|
|
if (c != primaryFunctionMap.end()) {
|
|
if (isSameSignature(clone, (FunctionNode *) *c)) {
|
|
return (FunctionNode *) *c;
|
|
}
|
|
else if (secondaryFunctionMap.contains(clone->name())) {
|
|
const NodeList& secs = secondaryFunctionMap[clone->name()];
|
|
NodeList::ConstIterator s = secs.begin();
|
|
while (s != secs.end()) {
|
|
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<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 InnerNode::secondaryKeys()
|
|
{
|
|
QStringList t;
|
|
QMap<QString, NodeList>::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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
In each child node that is a collision node,
|
|
clear the current child pointer.
|
|
*/
|
|
void InnerNode::clearCurrentChildPointers()
|
|
{
|
|
foreach (Node* child, childNodes()) {
|
|
if (child->subType() == Collision) {
|
|
child->clearCurrentChild();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void InnerNode::normalizeOverloads()
|
|
{
|
|
QMap<QString, Node *>::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.begin();
|
|
while (s != secs.end()) {
|
|
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<QString, Node *>::ConstIterator p = primaryFunctionMap.begin();
|
|
while (p != primaryFunctionMap.end()) {
|
|
FunctionNode *primaryFunc = (FunctionNode *) *p;
|
|
if (primaryFunc->isOverload())
|
|
primaryFunc->ove = false;
|
|
if (secondaryFunctionMap.contains(primaryFunc->name())) {
|
|
NodeList& secs = secondaryFunctionMap[primaryFunc->name()];
|
|
NodeList::ConstIterator s = secs.begin();
|
|
while (s != secs.end()) {
|
|
FunctionNode *secondaryFunc = (FunctionNode *) *s;
|
|
if (!secondaryFunc->isOverload())
|
|
secondaryFunc->ove = true;
|
|
++s;
|
|
}
|
|
}
|
|
++p;
|
|
}
|
|
|
|
NodeList::ConstIterator c = childNodes().begin();
|
|
while (c != childNodes().end()) {
|
|
if ((*c)->isInnerNode())
|
|
((InnerNode *) *c)->normalizeOverloads();
|
|
++c;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void InnerNode::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 InnerNode::deleteChildren()
|
|
{
|
|
NodeList childrenCopy = children; // `children` will be changed in ~Node()
|
|
qDeleteAll(childrenCopy);
|
|
}
|
|
|
|
/*! \fn bool InnerNode::isInnerNode() const
|
|
Returns true because this is an inner node.
|
|
*/
|
|
|
|
/*!
|
|
*/
|
|
const Node *InnerNode::findChildNodeByName(const QString& name) const
|
|
{
|
|
InnerNode *that = (InnerNode *) this;
|
|
return that->findChildNodeByName(name);
|
|
}
|
|
|
|
/*!
|
|
If \a qml is true, only match a node for which node->isQmlNode()
|
|
returns true. If \a qml is false, only match a node for which
|
|
node->isQmlNode() returns false.
|
|
*/
|
|
const Node* InnerNode::findChildNodeByName(const QString& name, bool qml) const
|
|
{
|
|
InnerNode*that = (InnerNode*) this;
|
|
return that->findChildNodeByName(name, qml);
|
|
}
|
|
|
|
/*!
|
|
Searches this node's children for a child named \a name
|
|
with the specified node \a type.
|
|
*/
|
|
const Node* InnerNode::findChildNodeByNameAndType(const QString& name, Type type) const
|
|
{
|
|
InnerNode *that = (InnerNode *) this;
|
|
return that->findChildNodeByNameAndType(name, type);
|
|
}
|
|
|
|
/*!
|
|
Find a function node that is a child of this nose, such
|
|
that the function node has the specified \a name. This
|
|
function calls the non-const version of itself.
|
|
*/
|
|
const FunctionNode *InnerNode::findFunctionNode(const QString& name) const
|
|
{
|
|
InnerNode *that = (InnerNode *) this;
|
|
return that->findFunctionNode(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. This function calls the non-const version.
|
|
*/
|
|
const FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) const
|
|
{
|
|
InnerNode *that = (InnerNode *) this;
|
|
return that->findFunctionNode(clone);
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
const EnumNode *InnerNode::findEnumNodeForValue(const QString &enumValue) const
|
|
{
|
|
foreach (const Node *node, enumChildren) {
|
|
const EnumNode *enume = static_cast<const EnumNode *>(node);
|
|
if (enume->hasItem(enumValue))
|
|
return enume;
|
|
}
|
|
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 Namespace:
|
|
setPageType(ApiPage);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Appends an \a include file to the list of include files.
|
|
*/
|
|
void InnerNode::addInclude(const QString& include)
|
|
{
|
|
inc.append(include);
|
|
}
|
|
|
|
/*!
|
|
Sets the list of include files to \a includes.
|
|
*/
|
|
void InnerNode::setIncludes(const QStringList& includes)
|
|
{
|
|
inc = 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<Parameter>::ConstIterator p1 = f1->parameters().begin();
|
|
QList<Parameter>::ConstIterator p2 = f2->parameters().begin();
|
|
while (p2 != f2->parameters().end()) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void InnerNode::removeChild(Node *child)
|
|
{
|
|
children.removeAll(child);
|
|
enumChildren.removeAll(child);
|
|
if (child->type() == Function) {
|
|
QMap<QString, Node *>::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<QString, Node *>::Iterator ent = childMap.find(child->name());
|
|
while (ent != childMap.end() && ent.key() == child->name()) {
|
|
if (*ent == child) {
|
|
childMap.erase(ent);
|
|
break;
|
|
}
|
|
++ent;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Find the module (QtCore, QtGui, 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 (!mod.isEmpty())
|
|
return mod;
|
|
|
|
QString path = location().filePath();
|
|
QString pattern = QString("src") + QDir::separator();
|
|
int start = path.lastIndexOf(pattern);
|
|
|
|
if (start == -1)
|
|
return "";
|
|
|
|
QString moduleDir = path.mid(start + pattern.size());
|
|
int finish = moduleDir.indexOf(QDir::separator());
|
|
|
|
if (finish == -1)
|
|
return "";
|
|
|
|
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 "";
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void InnerNode::removeRelated(Node *pseudoChild)
|
|
{
|
|
related_.removeAll(pseudoChild);
|
|
}
|
|
|
|
/*!
|
|
\class LeafNode
|
|
*/
|
|
|
|
/*! \fn bool LeafNode::isInnerNode() const
|
|
Returns 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:
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
hidden = false;
|
|
abstract = false;
|
|
qmlelement = 0;
|
|
setPageType(ApiPage);
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void ClassNode::addBaseClass(Access access,
|
|
ClassNode *node,
|
|
const QString &dataTypeWithTemplateArgs)
|
|
{
|
|
bases.append(RelatedClass(access, node, dataTypeWithTemplateArgs));
|
|
node->derived.append(RelatedClass(access, this));
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
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->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->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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Search the child list to find the property node with the
|
|
specified \a name.
|
|
*/
|
|
PropertyNode* ClassNode::findPropertyNode(const QString& name)
|
|
{
|
|
Node* n = findChildNodeByNameAndType(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;
|
|
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;
|
|
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<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;
|
|
}
|
|
|
|
QMap<QString, FakeNode*> FakeNode::qmlModuleMap_;
|
|
|
|
/*!
|
|
\class FakeNode
|
|
*/
|
|
|
|
/*!
|
|
The type of a FakeNode is Fake, and it has a \a subtype,
|
|
which specifies the type of FakeNode. The page type for
|
|
the page index is set here.
|
|
*/
|
|
FakeNode::FakeNode(InnerNode* parent, const QString& name, SubType subtype, Node::PageType ptype)
|
|
: InnerNode(Fake, parent, name), nodeSubtype_(subtype)
|
|
{
|
|
switch (subtype) {
|
|
case Page:
|
|
setPageType(ptype);
|
|
break;
|
|
case DitaMap:
|
|
setPageType(ptype);
|
|
break;
|
|
case Module:
|
|
case Group:
|
|
setPageType(OverviewPage);
|
|
break;
|
|
case QmlModule:
|
|
setPageType(OverviewPage);
|
|
break;
|
|
case QmlClass:
|
|
case QmlBasicType:
|
|
setPageType(ApiPage);
|
|
break;
|
|
case Example:
|
|
setPageType(ExamplePage);
|
|
break;
|
|
case Collision:
|
|
setPageType(ptype);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Returns the fake node's title. This is used for the page title.
|
|
*/
|
|
QString FakeNode::title() const
|
|
{
|
|
return title_;
|
|
}
|
|
|
|
/*!
|
|
Returns the fake node's full title, which is usually
|
|
just title(), but for some SubType values is different
|
|
from title()
|
|
*/
|
|
QString FakeNode::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 FakeNode::subTitle() const
|
|
{
|
|
if (!subtitle_.isEmpty())
|
|
return subtitle_;
|
|
|
|
if ((nodeSubtype_ == File) || (nodeSubtype_ == Image)) {
|
|
if (title().isEmpty() && name().contains(QLatin1Char('/')))
|
|
return name();
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
/*!
|
|
The QML module map contains an entry for each QML module
|
|
identifier. A QML module identifier is constucted from the
|
|
QML module name and the module's major version number, like
|
|
this: \e {<module-name><major-version>}
|
|
|
|
If the QML module map does not contain the module identifier
|
|
\a qmid, insert the QML module node \a fn mapped to \a qmid.
|
|
*/
|
|
void FakeNode::insertQmlModuleNode(const QString& qmid, FakeNode* fn)
|
|
{
|
|
if (!qmlModuleMap_.contains(qmid))
|
|
qmlModuleMap_.insert(qmid,fn);
|
|
}
|
|
|
|
/*!
|
|
Returns a pointer to the QML module node (FakeNode) that is
|
|
mapped to the QML module identifier constructed from \a arg.
|
|
If that QML module node does not yet exist, it is constructed
|
|
and inserted into the QML module map mapped to the QML module
|
|
identifier constructed from \a arg.
|
|
*/
|
|
FakeNode* FakeNode::lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg)
|
|
{
|
|
QStringList dotSplit;
|
|
QStringList blankSplit = arg.first.split(QLatin1Char(' '));
|
|
QString qmid = blankSplit[0];
|
|
if (blankSplit.size() > 1) {
|
|
dotSplit = blankSplit[1].split(QLatin1Char('.'));
|
|
qmid += dotSplit[0];
|
|
}
|
|
FakeNode* fn = 0;
|
|
if (qmlModuleMap_.contains(qmid))
|
|
fn = qmlModuleMap_.value(qmid);
|
|
if (!fn) {
|
|
fn = new FakeNode(tree->root(), arg.first, Node::QmlModule, Node::OverviewPage);
|
|
fn->setQmlModule(arg);
|
|
insertQmlModuleNode(qmid,fn);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
/*!
|
|
The constructor calls the FakeNode constructor with
|
|
\a parent, \a name, and Node::Example.
|
|
*/
|
|
ExampleNode::ExampleNode(InnerNode* parent, const QString& name)
|
|
: FakeNode(parent, name, Node::Example, Node::ExamplePage)
|
|
{
|
|
// nothing
|
|
}
|
|
|
|
/*!
|
|
\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)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
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)
|
|
{
|
|
// nothing.
|
|
}
|
|
|
|
/*!
|
|
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<Parameter>::Iterator t = params.begin();
|
|
QList<Parameter>::ConstIterator s = source->params.begin();
|
|
while (s != source->params.end() && 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<Parameter>::ConstIterator p = parameters().begin();
|
|
while (p != parameters().end()) {
|
|
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<Parameter>::ConstIterator p = parameters().begin();
|
|
while (p != parameters().end()) {
|
|
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; i<p; i++) {
|
|
s += params[i];
|
|
if (i < (p-1))
|
|
s += ", ";
|
|
}
|
|
}
|
|
s += QLatin1Char(')');
|
|
return s;
|
|
}
|
|
|
|
/*!
|
|
Print some debugging stuff.
|
|
*/
|
|
void FunctionNode::debug() const
|
|
{
|
|
qDebug("QML METHOD %s rt %s pp %s",
|
|
qPrintable(name()), qPrintable(rt), qPrintable(pp.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(InnerNode *parent, const QString& name)
|
|
: LeafNode(Property, parent, name),
|
|
stored_(FlagValueDefault),
|
|
designable_(FlagValueDefault),
|
|
scriptable_(FlagValueDefault),
|
|
writable_(FlagValueDefault),
|
|
user_(FlagValueDefault),
|
|
cst(false),
|
|
fnl(false),
|
|
rev(-1),
|
|
overrides(0)
|
|
{
|
|
// nothing.
|
|
}
|
|
|
|
/*!
|
|
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 (funcs[i].isEmpty())
|
|
funcs[i] = baseProperty->funcs[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<QString,Node*> QmlClassNode::inheritedBy;
|
|
QMap<QString, QmlClassNode*> QmlClassNode::qmlModuleMemberMap_;
|
|
|
|
/*!
|
|
Constructs a Qml class node (i.e. a Fake node with the
|
|
subtype QmlClass. The new node has the given \a parent
|
|
and \a name and is associated with the C++ class node
|
|
specified by \a cn which may be null if the the Qml
|
|
class node is not associated with a C++ class node.
|
|
*/
|
|
QmlClassNode::QmlClassNode(InnerNode *parent,
|
|
const QString& name,
|
|
ClassNode* cn)
|
|
: FakeNode(parent, name, QmlClass, Node::ApiPage),
|
|
abstract(false),
|
|
cnode_(cn),
|
|
base_(0)
|
|
{
|
|
int i = 0;
|
|
if (name.startsWith("QML:")) {
|
|
qDebug() << "BOGUS:" << name;
|
|
i = 4;
|
|
}
|
|
setTitle(name.mid(i));
|
|
}
|
|
|
|
/*!
|
|
Needed for printing a debug messages.
|
|
*/
|
|
QmlClassNode::~QmlClassNode()
|
|
{
|
|
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
|
|
qDebug() << "Deleting QmlClassNode:" << name();
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
Clear the static maps so that subsequent runs don't try to use
|
|
contents from a previous run.
|
|
*/
|
|
void QmlClassNode::terminate()
|
|
{
|
|
inheritedBy.clear();
|
|
qmlModuleMemberMap_.clear();
|
|
}
|
|
|
|
/*!
|
|
Insert the QML type node \a qcn into the static QML module
|
|
member map. The key is \a qmid + "::" + qcn->name().
|
|
*/
|
|
void QmlClassNode::insertQmlModuleMember(const QString& qmid, QmlClassNode* qcn)
|
|
{
|
|
qmlModuleMemberMap_.insert(qmid + "::" + qcn->name(), qcn);
|
|
}
|
|
|
|
/*!
|
|
Lookup the QML type node identified by the Qml module id
|
|
\a qmid and QML type \a name, and return a pointer to the
|
|
node. The key is \a qmid + "::" + qcn->name().
|
|
*/
|
|
QmlClassNode* QmlClassNode::lookupQmlTypeNode(const QString& qmid, const QString& name)
|
|
{
|
|
return qmlModuleMemberMap_.value(qmid + "::" + name);
|
|
}
|
|
|
|
/*!
|
|
The base file name for this kind of node has "qml_"
|
|
prepended to it.
|
|
|
|
But not yet. Still testing.
|
|
*/
|
|
QString QmlClassNode::fileBase() const
|
|
{
|
|
return Node::fileBase();
|
|
}
|
|
|
|
/*!
|
|
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.find(base,sub) == inheritedBy.end()) {
|
|
inheritedBy.insert(base,sub);
|
|
}
|
|
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
|
|
qDebug() << "QmlClassNode::addInheritedBy(): insert" << base << sub->name() << inheritedBy.size();
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
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);
|
|
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
|
|
qDebug() << "QmlClassNode::subclasses():" << inheritedBy.count(base) << base
|
|
<< "subs:" << subs.size() << "total size:" << inheritedBy.size();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*! \fn QString QmlClassNode::qmlModuleIdentifier() const
|
|
This function is called to get a string that is used either
|
|
as a prefix for the file name to use for QML element or
|
|
component reference page, or as a qualifier to prefix a
|
|
reference to a QML element or comnponent. The string that
|
|
is returned is the concatenation of the QML module name
|
|
and its version number. e.g., if an element or component
|
|
is defined to be in the QML module QtQuick 1, its module
|
|
identifier is "QtQuick1". See setQmlModule().
|
|
*/
|
|
|
|
/*!
|
|
This function splits \a arg on the blank character to get a
|
|
QML module name and version number. It then spilts the version
|
|
number on the '.' character to get a major version number and
|
|
a minor vrsion number. Both version numbers must be present.
|
|
It stores these components separately. If all three are found,
|
|
true is returned. If any of the three is not found or is not
|
|
correct, false is returned.
|
|
*/
|
|
bool Node::setQmlModule(const ArgLocPair& arg)
|
|
{
|
|
QStringList dotSplit;
|
|
QStringList blankSplit = arg.first.split(QLatin1Char(' '));
|
|
qmlModuleName_ = blankSplit[0];
|
|
qmlModuleVersionMajor_ = "1";
|
|
qmlModuleVersionMinor_ = "0";
|
|
if (blankSplit.size() > 1) {
|
|
dotSplit = blankSplit[1].split(QLatin1Char('.'));
|
|
qmlModuleVersionMajor_ = dotSplit[0];
|
|
if (dotSplit.size() > 1) {
|
|
qmlModuleVersionMinor_ = dotSplit[1];
|
|
return true;
|
|
}
|
|
else
|
|
arg.second.warning(tr("Minor version number missing for '\\qmlmodule' or '\\inqmlmodule'; 0 assumed."));
|
|
}
|
|
else
|
|
arg.second.warning(tr("Module version number missing for '\\qmlmodule' or '\\inqmlmodule'; 1.0 assumed."));
|
|
return false;
|
|
}
|
|
|
|
/*!
|
|
The name of this QML class node might be the same as the
|
|
name of some other QML class node. If so, then this node's
|
|
parent will be a NameCollisionNode.This function sets the
|
|
NameCollisionNode's current child to this node. This is
|
|
important when outputing the documentation for this node,
|
|
when, for example, the documentation contains a link to
|
|
the page being output. We don't want to generate a link
|
|
to the disambiguation page if we can avoid it, and to be
|
|
able to avoid it, the NameCollisionNode must maintain the
|
|
current child pointer. That's the purpose of this function.
|
|
*/
|
|
void QmlClassNode::setCurrentChild()
|
|
{
|
|
if (parent()) {
|
|
InnerNode* n = parent();
|
|
if (n->subType() == Node::Collision)
|
|
n->setCurrentChild(this);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void QmlClassNode::clearCurrentChild()
|
|
{
|
|
if (parent()) {
|
|
InnerNode* n = parent();
|
|
if (n->subType() == Node::Collision)
|
|
n->clearCurrentChild();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Most QML elements don't have an \\inherits command in their
|
|
\\qmlclass command. This leaves qdoc bereft, when it tries
|
|
to output the line in the documentation that specifies the
|
|
QML element that a QML element inherits.
|
|
*/
|
|
void QmlClassNode::resolveInheritance(Tree* tree)
|
|
{
|
|
if (!links().empty() && links().contains(Node::InheritsLink)) {
|
|
QPair<QString,QString> linkPair;
|
|
linkPair = links()[Node::InheritsLink];
|
|
QStringList strList = linkPair.first.split("::");
|
|
Node* n = tree->findQmlClassNode(strList);
|
|
if (n) {
|
|
base_ = static_cast<FakeNode*>(n);
|
|
if (base_ && base_->subType() == Node::QmlClass) {
|
|
return;
|
|
}
|
|
}
|
|
if (base_ && base_->subType() == Node::Collision) {
|
|
const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(base_);
|
|
const NodeList& children = ncn->childNodes();
|
|
for (int i=0; i<importList_.size(); ++i) {
|
|
QString qmid = importList_.at(i).first + importList_.at(i).second;
|
|
for (int j=0; j<children.size(); ++j) {
|
|
if (qmid == children.at(j)->qmlModuleIdentifier()) {
|
|
base_ = static_cast<FakeNode*>(children.at(j));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
QString qmid = qmlModuleIdentifier();
|
|
for (int k=0; k<children.size(); ++k) {
|
|
if (qmid == children.at(k)->qmlModuleIdentifier()) {
|
|
base_ = static_cast<QmlClassNode*>(children.at(k));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (base_)
|
|
return;
|
|
}
|
|
if (cnode_) {
|
|
QmlClassNode* qcn = cnode_->findQmlBaseNode();
|
|
if (qcn != 0)
|
|
base_ = qcn;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*!
|
|
Constructs a Qml basic type node (i.e. a Fake node with
|
|
the subtype QmlBasicType. The new node has the given
|
|
\a parent and \a name.
|
|
*/
|
|
QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent,
|
|
const QString& name)
|
|
: FakeNode(parent, name, QmlBasicType, Node::ApiPage)
|
|
{
|
|
setTitle(name);
|
|
}
|
|
|
|
/*!
|
|
Constructor for the Qml property group node. \a parent is
|
|
always a QmlClassNode.
|
|
*/
|
|
QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent, const QString& name)
|
|
: FakeNode(parent, name, QmlPropertyGroup, Node::ApiPage)
|
|
{
|
|
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 QmlPropGroupNode::idNumber()
|
|
{
|
|
if (idNumber_ == -1)
|
|
idNumber_ = incPropertyGroupCount();
|
|
return QString().setNum(idNumber_);
|
|
}
|
|
|
|
|
|
/*!
|
|
Constructor for the QML property node, when the \a parent
|
|
is QML property group node. This constructor is only used
|
|
for creating QML property nodes for QML elements, i.e.
|
|
not for creating QML property nodes for QML components.
|
|
Hopefully, this constructor will become obsolete, so don't
|
|
use it unless one of the other two constructors can't be
|
|
used.
|
|
*/
|
|
QmlPropertyNode::QmlPropertyNode(QmlPropGroupNode *parent,
|
|
const QString& name,
|
|
const QString& type,
|
|
bool attached)
|
|
: LeafNode(QmlProperty, parent, name),
|
|
type_(type),
|
|
stored_(FlagValueDefault),
|
|
designable_(FlagValueDefault),
|
|
isdefault_(false),
|
|
attached_(attached),
|
|
readOnly_(FlagValueDefault)
|
|
{
|
|
setPageType(ApiPage);
|
|
}
|
|
|
|
/*!
|
|
Constructor for the QML property node, when the \a parent
|
|
is a QML class node.
|
|
*/
|
|
QmlPropertyNode::QmlPropertyNode(QmlClassNode *parent,
|
|
const QString& name,
|
|
const QString& type,
|
|
bool attached)
|
|
: LeafNode(QmlProperty, parent, name),
|
|
type_(type),
|
|
stored_(FlagValueDefault),
|
|
designable_(FlagValueDefault),
|
|
isdefault_(false),
|
|
attached_(attached),
|
|
readOnly_(FlagValueDefault)
|
|
{
|
|
setPageType(ApiPage);
|
|
}
|
|
|
|
/*!
|
|
Constructor for the QML property node, when the \a parent
|
|
is a QML property node. Strictly speaking, this is not the
|
|
way QML property nodes were originally meant to be built,
|
|
because this constructor has another QML property node as
|
|
its parent. But this constructor is useful for documenting
|
|
QML properties in QML components, i.e., when you override
|
|
the definition of a property with the \e{qmlproperty}
|
|
command. It actually uses the parent of \a parent as the
|
|
parent.
|
|
*/
|
|
QmlPropertyNode::QmlPropertyNode(QmlPropertyNode* parent,
|
|
const QString& name,
|
|
const QString& type,
|
|
bool attached)
|
|
: LeafNode(parent->parent(), QmlProperty, name),
|
|
type_(type),
|
|
stored_(FlagValueDefault),
|
|
designable_(FlagValueDefault),
|
|
isdefault_(false),
|
|
attached_(attached),
|
|
readOnly_(FlagValueDefault)
|
|
{
|
|
setPageType(ApiPage);
|
|
}
|
|
|
|
/*!
|
|
Returns true if a QML property or attached property is
|
|
read-only. The algorithm for figuring this out is long
|
|
amd tedious and almost certainly will break. It currently
|
|
doesn't work for qmlproperty bool PropertyChanges::explicit,
|
|
because the tokenizer gets confused on "explicit".
|
|
*/
|
|
bool QmlPropertyNode::isWritable(Tree* tree)
|
|
{
|
|
if (readOnly_ != FlagValueDefault) {
|
|
return !fromFlagValue(readOnly_, false);
|
|
}
|
|
|
|
PropertyNode* pn = correspondingProperty(tree);
|
|
if (pn) {
|
|
return pn->isWritable();
|
|
}
|
|
else {
|
|
location().warning(tr("Can't detect if QML property %1 is read-only; writable assumed.").arg(name()));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree)
|
|
{
|
|
PropertyNode* pn;
|
|
|
|
Node* n = parent();
|
|
while (n && n->subType() != Node::QmlClass)
|
|
n = n->parent();
|
|
if (n) {
|
|
QmlClassNode* qcn = static_cast<QmlClassNode*>(n);
|
|
ClassNode* cn = qcn->classNode();
|
|
if (cn) {
|
|
QStringList dotSplit = name().split(QChar('.'));
|
|
pn = cn->findPropertyNode(dotSplit[0]);
|
|
if (pn) {
|
|
if (dotSplit.size() > 1) {
|
|
// Find the C++ property corresponding to the QML property in
|
|
// the property group, <group>.<property>.
|
|
|
|
QStringList path(extractClassName(pn->qualifiedDataType()));
|
|
Node* nn = tree->findClassNode(path);
|
|
if (nn) {
|
|
ClassNode* cn = static_cast<ClassNode*>(nn);
|
|
PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
|
|
if (pn2)
|
|
return pn2; // Return the property for the QML property.
|
|
else
|
|
return pn; // Return the property for the QML group.
|
|
}
|
|
}
|
|
else
|
|
return pn;
|
|
}
|
|
else {
|
|
pn = cn->findPropertyNode(dotSplit[0]);
|
|
if (pn)
|
|
return pn;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \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)
|
|
: FakeNode(child->parent(), child->name(), Collision, Node::NoPageType)
|
|
{
|
|
setTitle("Name Collision: " + child->name());
|
|
addCollision(child);
|
|
current = 0;
|
|
}
|
|
|
|
/*!
|
|
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.
|
|
}
|
|
|
|
/*! \fn const InnerNode* NameCollisionNode::currentChild() const
|
|
Returns a pointer to the current child, which may be 0.
|
|
*/
|
|
|
|
/*! \fn void NameCollisionNode::setCurrentChild(InnerNode* child)
|
|
Sets the current child to \a child. The current child is
|
|
valid only within the file where it is defined.
|
|
*/
|
|
|
|
/*! \fn void NameCollisionNode::clearCurrentChild()
|
|
Sets the current child to 0. This should be called at the
|
|
end of each file, because the current child is only valid
|
|
within the file where the child is defined.
|
|
*/
|
|
|
|
/*!
|
|
Returns true if this collision node's current node is a QML node.
|
|
*/
|
|
bool NameCollisionNode::isQmlNode() const
|
|
{
|
|
if (current)
|
|
return current->isQmlNode();
|
|
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)
|
|
{
|
|
if (current) {
|
|
if (current->type() == t && current->subType() == st)
|
|
return current;
|
|
}
|
|
const NodeList& cn = childNodes();
|
|
NodeList::ConstIterator i = cn.begin();
|
|
while (i != cn.end()) {
|
|
if ((*i)->type() == t && (*i)->subType() == st)
|
|
return static_cast<InnerNode*>(*i);
|
|
++i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
This node is a name collision node. Find a child of this node
|
|
such that the child's QML module identifier matches origin's
|
|
QML module identifier. Return the matching node, or return this
|
|
node if there is no matching node.
|
|
*/
|
|
const Node* NameCollisionNode::applyModuleIdentifier(const Node* origin) const
|
|
{
|
|
if (origin && !origin->qmlModuleIdentifier().isEmpty()) {
|
|
const NodeList& cn = childNodes();
|
|
NodeList::ConstIterator i = cn.begin();
|
|
while (i != cn.end()) {
|
|
if ((*i)->type() == Node::Fake && (*i)->subType() == Node::QmlClass) {
|
|
if (origin->qmlModuleIdentifier() == (*i)->qmlModuleIdentifier())
|
|
return (*i);
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/*!
|
|
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->type() != Node::Fake) || (n->subType() != Node::QmlPropertyGroup)))
|
|
pieces.insert(0, n->name());
|
|
|
|
if ((n->type() == Node::Fake) && (n->subType() != Node::QmlPropertyGroup)) {
|
|
if ((n->subType() == Node::QmlClass) && !n->qmlModuleName().isEmpty())
|
|
pieces.insert(0, n->qmlModuleIdentifier());
|
|
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->type() == Node::Fake) && (n->subType() != Node::QmlClass))
|
|
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(" ","-");
|
|
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 += "-";
|
|
}
|
|
else if (u == ')') {
|
|
clean += "-";
|
|
}
|
|
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<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 = "";
|
|
/*
|
|
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 += "-" + tail;
|
|
}
|
|
else
|
|
qDebug() << "qdoc internal error: Operator missing from operators_ map:" << op;
|
|
}
|
|
else {
|
|
str += op;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (parent_) {
|
|
if (parent_->type() == Class)
|
|
str = "class-member-" + func->name();
|
|
else if (parent_->type() == Namespace)
|
|
str = "namespace-member-" + func->name();
|
|
else if (parent_->type() == Fake) {
|
|
if (parent_->subType() == QmlClass)
|
|
str = "qml-method-" + func->name();
|
|
else
|
|
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::Fake:
|
|
{
|
|
switch (subType()) {
|
|
case Node::QmlClass:
|
|
str = "qml-class-" + name();
|
|
break;
|
|
case Node::QmlPropertyGroup:
|
|
{
|
|
Node* n = const_cast<Node*>(this);
|
|
str = "qml-propertygroup-" + n->name();
|
|
}
|
|
break;
|
|
case Node::Page:
|
|
case Node::Group:
|
|
case Node::Module:
|
|
case Node::HeaderFile:
|
|
str = title();
|
|
if (str.isEmpty()) {
|
|
str = name();
|
|
if (str.endsWith(".html"))
|
|
str.remove(str.size()-5,5);
|
|
}
|
|
str.replace("/","-");
|
|
break;
|
|
case Node::File:
|
|
str = name();
|
|
str.replace("/","-");
|
|
break;
|
|
case Node::Example:
|
|
str = name();
|
|
str.replace("/","-");
|
|
break;
|
|
case Node::QmlBasicType:
|
|
str = "qml-basic-type-" + name();
|
|
break;
|
|
case Node::QmlModule:
|
|
str = "qml-module-" + name();
|
|
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::QmlProperty:
|
|
str = "qml-property-" + 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<const FunctionNode*>(this);
|
|
str = "qml-method-" + func->name();
|
|
if (func->overloadNumber() != 1)
|
|
str += "-" + 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;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|