Files correlati : cg0.exe cg0700a.msk cg0700b.msk cg3.exe cg4.exe Bug : Commento: Merge 1.0 libraries
2040 lines
69 KiB
C++
2040 lines
69 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE: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 The Qt Company. For licensing terms
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
** following information to ensure the GNU Lesser General Public License
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** 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$
|
|
**
|
|
****************************************************************************/
|
|
|
|
/*
|
|
tree.cpp
|
|
*/
|
|
|
|
#include <QDomDocument>
|
|
|
|
#include "atom.h"
|
|
#include "doc.h"
|
|
#include "htmlgenerator.h"
|
|
#include "location.h"
|
|
#include "node.h"
|
|
#include "text.h"
|
|
#include "tree.h"
|
|
|
|
#include <limits.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
struct InheritanceBound
|
|
{
|
|
Node::Access access;
|
|
QStringList basePath;
|
|
QString dataTypeWithTemplateArgs;
|
|
InnerNode *parent;
|
|
|
|
InheritanceBound()
|
|
: access(Node::Public) { }
|
|
InheritanceBound(Node::Access access0,
|
|
const QStringList& basePath0,
|
|
const QString &dataTypeWithTemplateArgs0,
|
|
InnerNode *parent)
|
|
: access(access0), basePath(basePath0),
|
|
dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0),
|
|
parent(parent) { }
|
|
};
|
|
|
|
struct Target
|
|
{
|
|
Node *node;
|
|
Atom *atom;
|
|
int priority;
|
|
};
|
|
|
|
typedef QMap<PropertyNode::FunctionRole, QString> RoleMap;
|
|
typedef QMap<PropertyNode *, RoleMap> PropertyMap;
|
|
typedef QMultiMap<QString, Node *> GroupMap;
|
|
typedef QMultiHash<QString, FakeNode *> FakeNodeHash;
|
|
typedef QMultiHash<QString, Target> TargetHash;
|
|
|
|
class TreePrivate
|
|
{
|
|
public:
|
|
QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap;
|
|
PropertyMap unresolvedPropertyMap;
|
|
GroupMap groupMap;
|
|
QMultiMap<QString, QString> publicGroupMap;
|
|
FakeNodeHash fakeNodesByTitle;
|
|
TargetHash targetHash;
|
|
QList<QPair<ClassNode*,QString> > basesList;
|
|
QList<QPair<FunctionNode*,QString> > relatedList;
|
|
};
|
|
|
|
/*!
|
|
\class Tree
|
|
*/
|
|
|
|
/*!
|
|
The default constructor is the only constructor.
|
|
*/
|
|
Tree::Tree()
|
|
: roo(0, "")
|
|
{
|
|
priv = new TreePrivate;
|
|
}
|
|
|
|
/*!
|
|
The destructor deletes the internal, private tree.
|
|
*/
|
|
Tree::~Tree()
|
|
{
|
|
delete priv;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags, const Node* self)
|
|
{
|
|
return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
|
|
relative,
|
|
findFlags,
|
|
self));
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
const Node* Tree::findNode(const QStringList &path,
|
|
const Node* start,
|
|
int findFlags,
|
|
const Node* self) const
|
|
{
|
|
const Node* current = start;
|
|
if (!current)
|
|
current = root();
|
|
|
|
do {
|
|
const Node *node = current;
|
|
int i;
|
|
|
|
for (i = 0; i < path.size(); ++i) {
|
|
if (node == 0 || !node->isInnerNode())
|
|
break;
|
|
|
|
const Node *next =
|
|
static_cast<const InnerNode*>(node)->findNode(path.at(i));
|
|
|
|
if (!next && (findFlags & SearchEnumValues) && i == path.size()-1)
|
|
next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i));
|
|
|
|
if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) {
|
|
NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
|
|
foreach (const Node *baseClass, baseClasses) {
|
|
next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
|
|
if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1)
|
|
next = static_cast<const InnerNode *>(baseClass)
|
|
->findEnumNodeForValue(path.at(i));
|
|
if (next)
|
|
break;
|
|
}
|
|
}
|
|
node = next;
|
|
}
|
|
if (node && i == path.size()
|
|
&& (!(findFlags & NonFunction) || node->type() != Node::Function
|
|
|| ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams)) {
|
|
if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) {
|
|
return node;
|
|
}
|
|
}
|
|
current = current->parent();
|
|
} while (current);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
Find the node with the specified \a path name of the
|
|
specified \a type.
|
|
*/
|
|
Node *Tree::findNode(const QStringList &path,
|
|
Node::Type type,
|
|
Node *relative,
|
|
int findFlags)
|
|
{
|
|
return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path,
|
|
type,
|
|
relative,
|
|
findFlags));
|
|
}
|
|
|
|
/*!
|
|
Find the node with the specified \a path name of the
|
|
specified \a type.
|
|
*/
|
|
const Node *Tree::findNode(const QStringList &path,
|
|
Node::Type type,
|
|
const Node *relative,
|
|
int findFlags) const
|
|
{
|
|
const Node *node = findNode(path, relative, findFlags);
|
|
if (node != 0 && node->type() == type)
|
|
return node;
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
FunctionNode *Tree::findFunctionNode(const QStringList& path,
|
|
Node *relative,
|
|
int findFlags)
|
|
{
|
|
return const_cast<FunctionNode *>(
|
|
const_cast<const Tree *>(this)->findFunctionNode(path,
|
|
relative,
|
|
findFlags));
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
const FunctionNode *Tree::findFunctionNode(const QStringList &path,
|
|
const Node *relative,
|
|
int findFlags) const
|
|
{
|
|
if (!relative)
|
|
relative = root();
|
|
|
|
do {
|
|
const Node *node = relative;
|
|
int i;
|
|
|
|
for (i = 0; i < path.size(); ++i) {
|
|
if (node == 0 || !node->isInnerNode())
|
|
break;
|
|
|
|
const Node *next;
|
|
if (i == path.size() - 1)
|
|
next = ((InnerNode *) node)->findFunctionNode(path.at(i));
|
|
else
|
|
next = ((InnerNode *) node)->findNode(path.at(i));
|
|
|
|
if (!next && node->type() == Node::Class &&
|
|
(findFlags & SearchBaseClasses)) {
|
|
NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node));
|
|
foreach (const Node *baseClass, baseClasses) {
|
|
if (i == path.size() - 1)
|
|
next = static_cast<const InnerNode *>(baseClass)->findFunctionNode(path.at(i));
|
|
else
|
|
next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i));
|
|
|
|
if (next)
|
|
break;
|
|
}
|
|
}
|
|
|
|
node = next;
|
|
}
|
|
if (node && i == path.size() && node->isFunction()) {
|
|
// CppCodeParser::processOtherMetaCommand ensures that reimplemented
|
|
// functions are private.
|
|
const FunctionNode *func = static_cast<const FunctionNode*>(node);
|
|
while (func->access() == Node::Private) {
|
|
const FunctionNode *from = func->reimplementedFrom();
|
|
if (from != 0) {
|
|
if (from->access() != Node::Private)
|
|
return from;
|
|
else
|
|
func = from;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return func;
|
|
}
|
|
relative = relative->parent();
|
|
} while (relative);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
|
|
const FunctionNode *clone,
|
|
Node *relative,
|
|
int findFlags)
|
|
{
|
|
return const_cast<FunctionNode *>(
|
|
const_cast<const Tree *>(this)->findFunctionNode(parentPath,
|
|
clone,
|
|
relative,
|
|
findFlags));
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath,
|
|
const FunctionNode *clone,
|
|
const Node *relative,
|
|
int findFlags) const
|
|
{
|
|
const Node *parent = findNode(parentPath, relative, findFlags);
|
|
if (parent == 0 || !parent->isInnerNode()) {
|
|
return 0;
|
|
}
|
|
else {
|
|
return ((InnerNode *)parent)->findFunctionNode(clone);
|
|
}
|
|
}
|
|
|
|
static const int NumSuffixes = 3;
|
|
static const char * const suffixes[NumSuffixes] = { "", "s", "es" };
|
|
|
|
/*!
|
|
*/
|
|
const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const
|
|
{
|
|
for (int pass = 0; pass < NumSuffixes; ++pass) {
|
|
FakeNodeHash::const_iterator i =
|
|
priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass]));
|
|
if (i != priv->fakeNodesByTitle.constEnd()) {
|
|
FakeNodeHash::const_iterator j = i;
|
|
++j;
|
|
if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) {
|
|
QList<Location> internalLocations;
|
|
while (j != priv->fakeNodesByTitle.constEnd()) {
|
|
if (j.key() == i.key() && j.value()->url().isEmpty())
|
|
internalLocations.append(j.value()->doc().location());
|
|
++j;
|
|
}
|
|
if (internalLocations.size() > 0) {
|
|
i.value()->doc().location().warning(
|
|
tr("Page '%1' defined in more than one location:").arg(title));
|
|
foreach (const Location &location, internalLocations)
|
|
location.warning(tr("(defined here)"));
|
|
}
|
|
}
|
|
return i.value();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
const Node*
|
|
Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const
|
|
{
|
|
Target bestTarget = {0, 0, INT_MAX};
|
|
int numBestTargets = 0;
|
|
|
|
for (int pass = 0; pass < NumSuffixes; ++pass) {
|
|
TargetHash::const_iterator i =
|
|
priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass]));
|
|
if (i != priv->targetHash.constEnd()) {
|
|
TargetHash::const_iterator j = i;
|
|
do {
|
|
const Target &candidate = j.value();
|
|
if (candidate.priority < bestTarget.priority) {
|
|
bestTarget = candidate;
|
|
numBestTargets = 1;
|
|
} else if (candidate.priority == bestTarget.priority) {
|
|
++numBestTargets;
|
|
}
|
|
++j;
|
|
} while (j != priv->targetHash.constEnd() && j.key() == i.key());
|
|
|
|
if (numBestTargets == 1) {
|
|
atom = bestTarget.atom;
|
|
return bestTarget.node;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
Atom *Tree::findTarget(const QString &target, const Node *node) const
|
|
{
|
|
for (int pass = 0; pass < NumSuffixes; ++pass) {
|
|
QString key = Doc::canonicalTitle(target + suffixes[pass]);
|
|
TargetHash::const_iterator i = priv->targetHash.find(key);
|
|
|
|
if (i != priv->targetHash.constEnd()) {
|
|
do {
|
|
if (i.value().node == node)
|
|
return i.value().atom;
|
|
++i;
|
|
} while (i != priv->targetHash.constEnd() && i.key() == key);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::addBaseClass(ClassNode *subclass, Node::Access access,
|
|
const QStringList &basePath,
|
|
const QString &dataTypeWithTemplateArgs,
|
|
InnerNode *parent)
|
|
{
|
|
priv->unresolvedInheritanceMap[subclass].append(
|
|
InheritanceBound(access,
|
|
basePath,
|
|
dataTypeWithTemplateArgs,
|
|
parent)
|
|
);
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void Tree::addPropertyFunction(PropertyNode *property,
|
|
const QString &funcName,
|
|
PropertyNode::FunctionRole funcRole)
|
|
{
|
|
priv->unresolvedPropertyMap[property].insert(funcRole, funcName);
|
|
}
|
|
|
|
/*!
|
|
This function adds the \a node to the \a group. The group
|
|
can be listed anywhere using the \e{annotated list} command.
|
|
*/
|
|
void Tree::addToGroup(Node *node, const QString &group)
|
|
{
|
|
priv->groupMap.insert(group, node);
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
QMultiMap<QString, Node *> Tree::groups() const
|
|
{
|
|
return priv->groupMap;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::addToPublicGroup(Node *node, const QString &group)
|
|
{
|
|
priv->publicGroupMap.insert(node->name(), group);
|
|
addToGroup(node, group);
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
QMultiMap<QString, QString> Tree::publicGroups() const
|
|
{
|
|
return priv->publicGroupMap;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveInheritance(NamespaceNode *rootNode)
|
|
{
|
|
if (!rootNode)
|
|
rootNode = root();
|
|
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
NodeList::ConstIterator c = rootNode->childNodes().begin();
|
|
while (c != rootNode->childNodes().end()) {
|
|
if ((*c)->type() == Node::Class) {
|
|
resolveInheritance(pass, (ClassNode *) *c);
|
|
}
|
|
else if ((*c)->type() == Node::Namespace) {
|
|
NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
|
|
resolveInheritance(ns);
|
|
}
|
|
++c;
|
|
}
|
|
if (rootNode == root())
|
|
priv->unresolvedInheritanceMap.clear();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveProperties()
|
|
{
|
|
PropertyMap::ConstIterator propEntry;
|
|
|
|
propEntry = priv->unresolvedPropertyMap.begin();
|
|
while (propEntry != priv->unresolvedPropertyMap.end()) {
|
|
PropertyNode *property = propEntry.key();
|
|
InnerNode *parent = property->parent();
|
|
QString getterName = (*propEntry)[PropertyNode::Getter];
|
|
QString setterName = (*propEntry)[PropertyNode::Setter];
|
|
QString resetterName = (*propEntry)[PropertyNode::Resetter];
|
|
QString notifierName = (*propEntry)[PropertyNode::Notifier];
|
|
|
|
NodeList::ConstIterator c = parent->childNodes().begin();
|
|
while (c != parent->childNodes().end()) {
|
|
if ((*c)->type() == Node::Function) {
|
|
FunctionNode *function = static_cast<FunctionNode *>(*c);
|
|
if (function->access() == property->access() &&
|
|
(function->status() == property->status() ||
|
|
function->doc().isEmpty())) {
|
|
if (function->name() == getterName) {
|
|
property->addFunction(function, PropertyNode::Getter);
|
|
} else if (function->name() == setterName) {
|
|
property->addFunction(function, PropertyNode::Setter);
|
|
} else if (function->name() == resetterName) {
|
|
property->addFunction(function, PropertyNode::Resetter);
|
|
} else if (function->name() == notifierName) {
|
|
property->addSignal(function, PropertyNode::Notifier);
|
|
}
|
|
}
|
|
}
|
|
++c;
|
|
}
|
|
++propEntry;
|
|
}
|
|
|
|
propEntry = priv->unresolvedPropertyMap.begin();
|
|
while (propEntry != priv->unresolvedPropertyMap.end()) {
|
|
PropertyNode *property = propEntry.key();
|
|
// redo it to set the property functions
|
|
if (property->overriddenFrom())
|
|
property->setOverriddenFrom(property->overriddenFrom());
|
|
++propEntry;
|
|
}
|
|
|
|
priv->unresolvedPropertyMap.clear();
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveInheritance(int pass, ClassNode *classe)
|
|
{
|
|
if (pass == 0) {
|
|
QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe];
|
|
QList<InheritanceBound>::ConstIterator b = bounds.begin();
|
|
while (b != bounds.end()) {
|
|
ClassNode *baseClass = (ClassNode*)findNode((*b).basePath,
|
|
Node::Class);
|
|
if (!baseClass && (*b).parent) {
|
|
baseClass = (ClassNode*)findNode((*b).basePath,
|
|
Node::Class,
|
|
(*b).parent);
|
|
}
|
|
if (baseClass) {
|
|
classe->addBaseClass((*b).access,
|
|
baseClass,
|
|
(*b).dataTypeWithTemplateArgs);
|
|
}
|
|
++b;
|
|
}
|
|
}
|
|
else {
|
|
NodeList::ConstIterator c = classe->childNodes().begin();
|
|
while (c != classe->childNodes().end()) {
|
|
if ((*c)->type() == Node::Function) {
|
|
FunctionNode *func = (FunctionNode *) *c;
|
|
FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func);
|
|
if (from != 0) {
|
|
if (func->virtualness() == FunctionNode::NonVirtual)
|
|
func->setVirtualness(FunctionNode::ImpureVirtual);
|
|
func->setReimplementedFrom(from);
|
|
}
|
|
}
|
|
else if ((*c)->type() == Node::Property) {
|
|
fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c));
|
|
}
|
|
++c;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveGroups()
|
|
{
|
|
GroupMap::const_iterator i;
|
|
QString prevGroup;
|
|
for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) {
|
|
if (i.value()->access() == Node::Private)
|
|
continue;
|
|
|
|
FakeNode *fake =
|
|
static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake));
|
|
if (fake && fake->subType() == Node::Group) {
|
|
fake->addGroupMember(i.value());
|
|
}
|
|
|
|
prevGroup = i.key();
|
|
}
|
|
|
|
//priv->groupMap.clear();
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveTargets()
|
|
{
|
|
// need recursion
|
|
|
|
foreach (Node *child, roo.childNodes()) {
|
|
if (child->type() == Node::Fake) {
|
|
FakeNode *node = static_cast<FakeNode *>(child);
|
|
priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node);
|
|
}
|
|
|
|
if (child->doc().hasTableOfContents()) {
|
|
const QList<Atom *> &toc = child->doc().tableOfContents();
|
|
Target target;
|
|
target.node = child;
|
|
target.priority = 3;
|
|
|
|
for (int i = 0; i < toc.size(); ++i) {
|
|
target.atom = toc.at(i);
|
|
QString title = Text::sectionHeading(target.atom).toString();
|
|
if (!title.isEmpty())
|
|
priv->targetHash.insert(Doc::canonicalTitle(title), target);
|
|
}
|
|
}
|
|
if (child->doc().hasKeywords()) {
|
|
const QList<Atom *> &keywords = child->doc().keywords();
|
|
Target target;
|
|
target.node = child;
|
|
target.priority = 1;
|
|
|
|
for (int i = 0; i < keywords.size(); ++i) {
|
|
target.atom = keywords.at(i);
|
|
priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
|
|
}
|
|
}
|
|
if (child->doc().hasTargets()) {
|
|
const QList<Atom *> &toc = child->doc().targets();
|
|
Target target;
|
|
target.node = child;
|
|
target.priority = 2;
|
|
|
|
for (int i = 0; i < toc.size(); ++i) {
|
|
target.atom = toc.at(i);
|
|
priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::fixInheritance(NamespaceNode *rootNode)
|
|
{
|
|
if (!rootNode)
|
|
rootNode = root();
|
|
|
|
NodeList::ConstIterator c = rootNode->childNodes().begin();
|
|
while (c != rootNode->childNodes().end()) {
|
|
if ((*c)->type() == Node::Class)
|
|
static_cast<ClassNode *>(*c)->fixBaseClasses();
|
|
else if ((*c)->type() == Node::Namespace) {
|
|
NamespaceNode *ns = static_cast<NamespaceNode*>(*c);
|
|
fixInheritance(ns);
|
|
}
|
|
++c;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe,
|
|
FunctionNode *clone)
|
|
{
|
|
QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin();
|
|
while (r != classe->baseClasses().end()) {
|
|
FunctionNode *func;
|
|
if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 ||
|
|
(func = (*r).node->findFunctionNode(clone)) != 0)) {
|
|
if (func->virtualness() != FunctionNode::NonVirtual)
|
|
return func;
|
|
}
|
|
++r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::fixPropertyUsingBaseClasses(ClassNode *classe,
|
|
PropertyNode *property)
|
|
{
|
|
QList<RelatedClass>::const_iterator r = classe->baseClasses().begin();
|
|
while (r != classe->baseClasses().end()) {
|
|
PropertyNode *baseProperty =
|
|
static_cast<PropertyNode *>(r->node->findNode(property->name(),
|
|
Node::Property));
|
|
if (baseProperty) {
|
|
fixPropertyUsingBaseClasses(r->node, baseProperty);
|
|
property->setOverriddenFrom(baseProperty);
|
|
}
|
|
else {
|
|
fixPropertyUsingBaseClasses(r->node, property);
|
|
}
|
|
++r;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
NodeList Tree::allBaseClasses(const ClassNode *classe) const
|
|
{
|
|
NodeList result;
|
|
foreach (const RelatedClass &r, classe->baseClasses()) {
|
|
result += r.node;
|
|
result += allBaseClasses(r.node);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::readIndexes(const QStringList &indexFiles)
|
|
{
|
|
foreach (const QString &indexFile, indexFiles)
|
|
readIndexFile(indexFile);
|
|
}
|
|
|
|
/*!
|
|
Read the QDomDocument at \a path and get the index from it.
|
|
*/
|
|
void Tree::readIndexFile(const QString &path)
|
|
{
|
|
QFile file(path);
|
|
if (file.open(QFile::ReadOnly)) {
|
|
QDomDocument document;
|
|
document.setContent(&file);
|
|
file.close();
|
|
|
|
QDomElement indexElement = document.documentElement();
|
|
QString indexUrl = indexElement.attribute("url", "");
|
|
priv->basesList.clear();
|
|
priv->relatedList.clear();
|
|
|
|
// Scan all elements in the XML file, constructing a map that contains
|
|
// base classes for each class found.
|
|
|
|
QDomElement child = indexElement.firstChildElement();
|
|
while (!child.isNull()) {
|
|
readIndexSection(child, root(), indexUrl);
|
|
child = child.nextSiblingElement();
|
|
}
|
|
|
|
// Now that all the base classes have been found for this index,
|
|
// arrange them into an inheritance hierarchy.
|
|
|
|
resolveIndex();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::readIndexSection(const QDomElement &element,
|
|
InnerNode *parent,
|
|
const QString &indexUrl)
|
|
{
|
|
QString name = element.attribute("name");
|
|
QString href = element.attribute("href");
|
|
|
|
Node *section;
|
|
Location location;
|
|
|
|
if (element.nodeName() == "namespace") {
|
|
section = new NamespaceNode(parent, name);
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location = Location(indexUrl + "/" + name.toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(name.toLower() + ".html");
|
|
|
|
}
|
|
else if (element.nodeName() == "class") {
|
|
section = new ClassNode(parent, name);
|
|
priv->basesList.append(QPair<ClassNode*,QString>(
|
|
static_cast<ClassNode*>(section), element.attribute("bases")));
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location = Location(indexUrl + "/" + name.toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(name.toLower() + ".html");
|
|
|
|
}
|
|
else if (element.nodeName() == "page") {
|
|
Node::SubType subtype;
|
|
if (element.attribute("subtype") == "example")
|
|
subtype = Node::Example;
|
|
else if (element.attribute("subtype") == "header")
|
|
subtype = Node::HeaderFile;
|
|
else if (element.attribute("subtype") == "file")
|
|
subtype = Node::File;
|
|
else if (element.attribute("subtype") == "group")
|
|
subtype = Node::Group;
|
|
else if (element.attribute("subtype") == "module")
|
|
subtype = Node::Module;
|
|
else if (element.attribute("subtype") == "page")
|
|
subtype = Node::Page;
|
|
else if (element.attribute("subtype") == "externalpage")
|
|
subtype = Node::ExternalPage;
|
|
else if (element.attribute("subtype") == "qmlclass")
|
|
subtype = Node::QmlClass;
|
|
else if (element.attribute("subtype") == "qmlpropertygroup")
|
|
subtype = Node::QmlPropertyGroup;
|
|
else if (element.attribute("subtype") == "qmlbasictype")
|
|
subtype = Node::QmlBasicType;
|
|
else
|
|
return;
|
|
|
|
FakeNode *fakeNode = new FakeNode(parent, name, subtype);
|
|
fakeNode->setTitle(element.attribute("title"));
|
|
|
|
if (element.hasAttribute("location"))
|
|
name = element.attribute("location", "");
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location = Location(indexUrl + "/" + name);
|
|
else if (!indexUrl.isNull())
|
|
location = Location(name);
|
|
|
|
section = fakeNode;
|
|
|
|
}
|
|
else if (element.nodeName() == "enum") {
|
|
EnumNode *enumNode = new EnumNode(parent, name);
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location =
|
|
Location(indexUrl + "/" + parent->name().toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(parent->name().toLower() + ".html");
|
|
|
|
QDomElement child = element.firstChildElement("value");
|
|
while (!child.isNull()) {
|
|
EnumItem item(child.attribute("name"), child.attribute("value"));
|
|
enumNode->addItem(item);
|
|
child = child.nextSiblingElement("value");
|
|
}
|
|
|
|
section = enumNode;
|
|
|
|
} else if (element.nodeName() == "typedef") {
|
|
section = new TypedefNode(parent, name);
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location =
|
|
Location(indexUrl + "/" + parent->name().toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(parent->name().toLower() + ".html");
|
|
|
|
}
|
|
else if (element.nodeName() == "property") {
|
|
section = new PropertyNode(parent, name);
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location =
|
|
Location(indexUrl + "/" + parent->name().toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(parent->name().toLower() + ".html");
|
|
|
|
} else if (element.nodeName() == "function") {
|
|
FunctionNode::Virtualness virt;
|
|
if (element.attribute("virtual") == "non")
|
|
virt = FunctionNode::NonVirtual;
|
|
else if (element.attribute("virtual") == "impure")
|
|
virt = FunctionNode::ImpureVirtual;
|
|
else if (element.attribute("virtual") == "pure")
|
|
virt = FunctionNode::PureVirtual;
|
|
else
|
|
return;
|
|
|
|
FunctionNode::Metaness meta;
|
|
if (element.attribute("meta") == "plain")
|
|
meta = FunctionNode::Plain;
|
|
else if (element.attribute("meta") == "signal")
|
|
meta = FunctionNode::Signal;
|
|
else if (element.attribute("meta") == "slot")
|
|
meta = FunctionNode::Slot;
|
|
else if (element.attribute("meta") == "constructor")
|
|
meta = FunctionNode::Ctor;
|
|
else if (element.attribute("meta") == "destructor")
|
|
meta = FunctionNode::Dtor;
|
|
else if (element.attribute("meta") == "macro")
|
|
meta = FunctionNode::MacroWithParams;
|
|
else if (element.attribute("meta") == "macrowithparams")
|
|
meta = FunctionNode::MacroWithParams;
|
|
else if (element.attribute("meta") == "macrowithoutparams")
|
|
meta = FunctionNode::MacroWithoutParams;
|
|
else
|
|
return;
|
|
|
|
FunctionNode *functionNode = new FunctionNode(parent, name);
|
|
functionNode->setReturnType(element.attribute("return"));
|
|
functionNode->setVirtualness(virt);
|
|
functionNode->setMetaness(meta);
|
|
functionNode->setConst(element.attribute("const") == "true");
|
|
functionNode->setStatic(element.attribute("static") == "true");
|
|
functionNode->setOverload(element.attribute("overload") == "true");
|
|
|
|
if (element.hasAttribute("relates")
|
|
&& element.attribute("relates") != parent->name()) {
|
|
priv->relatedList.append(
|
|
QPair<FunctionNode*,QString>(functionNode,
|
|
element.attribute("relates")));
|
|
}
|
|
|
|
QDomElement child = element.firstChildElement("parameter");
|
|
while (!child.isNull()) {
|
|
// Do not use the default value for the parameter; it is not
|
|
// required, and has been known to cause problems.
|
|
Parameter parameter(child.attribute("left"),
|
|
child.attribute("right"),
|
|
child.attribute("name"),
|
|
""); // child.attribute("default")
|
|
functionNode->addParameter(parameter);
|
|
child = child.nextSiblingElement("parameter");
|
|
}
|
|
|
|
section = functionNode;
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location =
|
|
Location(indexUrl + "/" + parent->name().toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(parent->name().toLower() + ".html");
|
|
|
|
}
|
|
else if (element.nodeName() == "variable") {
|
|
section = new VariableNode(parent, name);
|
|
|
|
if (!indexUrl.isEmpty())
|
|
location = Location(indexUrl + "/" + parent->name().toLower() + ".html");
|
|
else if (!indexUrl.isNull())
|
|
location = Location(parent->name().toLower() + ".html");
|
|
|
|
}
|
|
else if (element.nodeName() == "keyword") {
|
|
Target target;
|
|
target.node = parent;
|
|
target.priority = 1;
|
|
target.atom = new Atom(Atom::Target, name);
|
|
priv->targetHash.insert(name, target);
|
|
return;
|
|
|
|
}
|
|
else if (element.nodeName() == "target") {
|
|
Target target;
|
|
target.node = parent;
|
|
target.priority = 2;
|
|
target.atom = new Atom(Atom::Target, name);
|
|
priv->targetHash.insert(name, target);
|
|
return;
|
|
|
|
}
|
|
else if (element.nodeName() == "contents") {
|
|
Target target;
|
|
target.node = parent;
|
|
target.priority = 3;
|
|
target.atom = new Atom(Atom::Target, name);
|
|
priv->targetHash.insert(name, target);
|
|
return;
|
|
|
|
}
|
|
else
|
|
return;
|
|
|
|
QString access = element.attribute("access");
|
|
if (access == "public")
|
|
section->setAccess(Node::Public);
|
|
else if (access == "protected")
|
|
section->setAccess(Node::Protected);
|
|
else if (access == "private")
|
|
section->setAccess(Node::Private);
|
|
else
|
|
section->setAccess(Node::Public);
|
|
|
|
if (element.nodeName() != "page") {
|
|
QString threadSafety = element.attribute("threadsafety");
|
|
if (threadSafety == "non-reentrant")
|
|
section->setThreadSafeness(Node::NonReentrant);
|
|
else if (threadSafety == "reentrant")
|
|
section->setThreadSafeness(Node::Reentrant);
|
|
else if (threadSafety == "thread safe")
|
|
section->setThreadSafeness(Node::ThreadSafe);
|
|
else
|
|
section->setThreadSafeness(Node::UnspecifiedSafeness);
|
|
}
|
|
else
|
|
section->setThreadSafeness(Node::UnspecifiedSafeness);
|
|
|
|
QString status = element.attribute("status");
|
|
if (status == "compat")
|
|
section->setStatus(Node::Compat);
|
|
else if (status == "obsolete")
|
|
section->setStatus(Node::Obsolete);
|
|
else if (status == "deprecated")
|
|
section->setStatus(Node::Deprecated);
|
|
else if (status == "preliminary")
|
|
section->setStatus(Node::Preliminary);
|
|
else if (status == "commendable")
|
|
section->setStatus(Node::Commendable);
|
|
else if (status == "internal")
|
|
section->setStatus(Node::Internal);
|
|
else if (status == "main")
|
|
section->setStatus(Node::Main);
|
|
else
|
|
section->setStatus(Node::Commendable);
|
|
|
|
section->setModuleName(element.attribute("module"));
|
|
if (!indexUrl.isEmpty()) {
|
|
if (indexUrl.startsWith("."))
|
|
section->setUrl(href);
|
|
else
|
|
section->setUrl(indexUrl + "/" + href);
|
|
}
|
|
|
|
// Create some content for the node.
|
|
QSet<QString> emptySet;
|
|
|
|
Doc doc(location, location, " ", emptySet); // placeholder
|
|
section->setDoc(doc);
|
|
|
|
if (section->isInnerNode()) {
|
|
InnerNode *inner = static_cast<InnerNode*>(section);
|
|
if (inner) {
|
|
QDomElement child = element.firstChildElement();
|
|
|
|
while (!child.isNull()) {
|
|
if (element.nodeName() == "class")
|
|
readIndexSection(child, inner, indexUrl);
|
|
else if (element.nodeName() == "page")
|
|
readIndexSection(child, inner, indexUrl);
|
|
else if (element.nodeName() == "namespace" && !name.isEmpty())
|
|
// The root node in the index is a namespace with an empty name.
|
|
readIndexSection(child, inner, indexUrl);
|
|
else
|
|
readIndexSection(child, parent, indexUrl);
|
|
|
|
child = child.nextSiblingElement();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
QString Tree::readIndexText(const QDomElement &element)
|
|
{
|
|
QString text;
|
|
QDomNode child = element.firstChild();
|
|
while (!child.isNull()) {
|
|
if (child.isText())
|
|
text += child.toText().nodeValue();
|
|
child = child.nextSibling();
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::resolveIndex()
|
|
{
|
|
QPair<ClassNode*,QString> pair;
|
|
|
|
foreach (pair, priv->basesList) {
|
|
foreach (const QString &base, pair.second.split(",")) {
|
|
Node *baseClass = root()->findNode(base, Node::Class);
|
|
if (baseClass) {
|
|
pair.first->addBaseClass(Node::Public,
|
|
static_cast<ClassNode*>(baseClass));
|
|
}
|
|
}
|
|
}
|
|
|
|
QPair<FunctionNode*,QString> relatedPair;
|
|
|
|
foreach (relatedPair, priv->relatedList) {
|
|
Node *classNode = root()->findNode(relatedPair.second, Node::Class);
|
|
if (classNode)
|
|
relatedPair.first->setRelates(static_cast<ClassNode*>(classNode));
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Generate the index section with the given \a writer for the \a node
|
|
specified, returning true if an element was written; otherwise returns
|
|
false.
|
|
*/
|
|
bool Tree::generateIndexSection(QXmlStreamWriter &writer,
|
|
const Node *node,
|
|
bool generateInternalNodes) const
|
|
{
|
|
if (!node->url().isEmpty())
|
|
return false;
|
|
|
|
QString nodeName;
|
|
switch (node->type()) {
|
|
case Node::Namespace:
|
|
nodeName = "namespace";
|
|
break;
|
|
case Node::Class:
|
|
nodeName = "class";
|
|
break;
|
|
case Node::Fake:
|
|
nodeName = "page";
|
|
break;
|
|
case Node::Enum:
|
|
nodeName = "enum";
|
|
break;
|
|
case Node::Typedef:
|
|
nodeName = "typedef";
|
|
break;
|
|
case Node::Property:
|
|
nodeName = "property";
|
|
break;
|
|
case Node::Function:
|
|
nodeName = "function";
|
|
break;
|
|
case Node::Variable:
|
|
nodeName = "variable";
|
|
break;
|
|
case Node::Target:
|
|
nodeName = "target";
|
|
break;
|
|
case Node::QmlProperty:
|
|
nodeName = "qmlproperty";
|
|
break;
|
|
case Node::QmlSignal:
|
|
nodeName = "qmlsignal";
|
|
break;
|
|
case Node::QmlMethod:
|
|
nodeName = "qmlmethod";
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
QString access;
|
|
switch (node->access()) {
|
|
case Node::Public:
|
|
access = "public";
|
|
break;
|
|
case Node::Protected:
|
|
access = "protected";
|
|
break;
|
|
case Node::Private:
|
|
// Do not include private non-internal nodes in the index.
|
|
// (Internal public and protected nodes are marked as private
|
|
// by qdoc. We can check their internal status to determine
|
|
// whether they were really private to begin with.)
|
|
if (node->status() == Node::Internal && generateInternalNodes)
|
|
access = "internal";
|
|
else
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
QString objName = node->name();
|
|
|
|
// Special case: only the root node should have an empty name.
|
|
if (objName.isEmpty() && node != root())
|
|
return false;
|
|
|
|
writer.writeStartElement(nodeName);
|
|
|
|
QXmlStreamAttributes attributes;
|
|
writer.writeAttribute("access", access);
|
|
|
|
if (node->type() != Node::Fake) {
|
|
QString threadSafety;
|
|
switch (node->threadSafeness()) {
|
|
case Node::NonReentrant:
|
|
threadSafety = "non-reentrant";
|
|
break;
|
|
case Node::Reentrant:
|
|
threadSafety = "reentrant";
|
|
break;
|
|
case Node::ThreadSafe:
|
|
threadSafety = "thread safe";
|
|
break;
|
|
case Node::UnspecifiedSafeness:
|
|
default:
|
|
threadSafety = "unspecified";
|
|
break;
|
|
}
|
|
writer.writeAttribute("threadsafety", threadSafety);
|
|
}
|
|
|
|
QString status;
|
|
switch (node->status()) {
|
|
case Node::Compat:
|
|
status = "compat";
|
|
break;
|
|
case Node::Obsolete:
|
|
status = "obsolete";
|
|
break;
|
|
case Node::Deprecated:
|
|
status = "deprecated";
|
|
break;
|
|
case Node::Preliminary:
|
|
status = "preliminary";
|
|
break;
|
|
case Node::Commendable:
|
|
status = "commendable";
|
|
break;
|
|
case Node::Internal:
|
|
status = "internal";
|
|
break;
|
|
case Node::Main:
|
|
default:
|
|
status = "main";
|
|
break;
|
|
}
|
|
writer.writeAttribute("status", status);
|
|
|
|
writer.writeAttribute("name", objName);
|
|
QString fullName = fullDocumentName(node);
|
|
if (fullName != objName)
|
|
writer.writeAttribute("fullname", fullName);
|
|
writer.writeAttribute("href", HtmlGenerator::fullDocumentLocation(node));
|
|
if ((node->type() != Node::Fake) && (!node->isQmlNode()))
|
|
writer.writeAttribute("location", node->location().fileName());
|
|
|
|
switch (node->type()) {
|
|
|
|
case Node::Class:
|
|
{
|
|
// Classes contain information about their base classes.
|
|
|
|
const ClassNode *classNode = static_cast<const ClassNode*>(node);
|
|
QList<RelatedClass> bases = classNode->baseClasses();
|
|
QSet<QString> baseStrings;
|
|
foreach (const RelatedClass &related, bases) {
|
|
ClassNode *baseClassNode = related.node;
|
|
baseStrings.insert(baseClassNode->name());
|
|
}
|
|
writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
|
|
writer.writeAttribute("module", node->moduleName());
|
|
}
|
|
break;
|
|
|
|
case Node::Namespace:
|
|
writer.writeAttribute("module", node->moduleName());
|
|
break;
|
|
|
|
case Node::Fake:
|
|
{
|
|
/*
|
|
Fake nodes (such as manual pages) contain subtypes,
|
|
titles and other attributes.
|
|
*/
|
|
|
|
const FakeNode *fakeNode = static_cast<const FakeNode*>(node);
|
|
switch (fakeNode->subType()) {
|
|
case Node::Example:
|
|
writer.writeAttribute("subtype", "example");
|
|
break;
|
|
case Node::HeaderFile:
|
|
writer.writeAttribute("subtype", "header");
|
|
break;
|
|
case Node::File:
|
|
writer.writeAttribute("subtype", "file");
|
|
break;
|
|
case Node::Group:
|
|
writer.writeAttribute("subtype", "group");
|
|
break;
|
|
case Node::Module:
|
|
writer.writeAttribute("subtype", "module");
|
|
break;
|
|
case Node::Page:
|
|
writer.writeAttribute("subtype", "page");
|
|
break;
|
|
case Node::ExternalPage:
|
|
writer.writeAttribute("subtype", "externalpage");
|
|
break;
|
|
case Node::QmlClass:
|
|
writer.writeAttribute("subtype", "qmlclass");
|
|
break;
|
|
case Node::QmlBasicType:
|
|
writer.writeAttribute("subtype", "qmlbasictype");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
writer.writeAttribute("title", fakeNode->title());
|
|
writer.writeAttribute("fulltitle", fakeNode->fullTitle());
|
|
writer.writeAttribute("subtitle", fakeNode->subTitle());
|
|
writer.writeAttribute("location", fakeNode->doc().location().fileName());
|
|
}
|
|
break;
|
|
|
|
case Node::Function:
|
|
{
|
|
/*
|
|
Function nodes contain information about the type of
|
|
function being described.
|
|
*/
|
|
|
|
const FunctionNode *functionNode =
|
|
static_cast<const FunctionNode*>(node);
|
|
|
|
switch (functionNode->virtualness()) {
|
|
case FunctionNode::NonVirtual:
|
|
writer.writeAttribute("virtual", "non");
|
|
break;
|
|
case FunctionNode::ImpureVirtual:
|
|
writer.writeAttribute("virtual", "impure");
|
|
break;
|
|
case FunctionNode::PureVirtual:
|
|
writer.writeAttribute("virtual", "pure");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch (functionNode->metaness()) {
|
|
case FunctionNode::Plain:
|
|
writer.writeAttribute("meta", "plain");
|
|
break;
|
|
case FunctionNode::Signal:
|
|
writer.writeAttribute("meta", "signal");
|
|
break;
|
|
case FunctionNode::Slot:
|
|
writer.writeAttribute("meta", "slot");
|
|
break;
|
|
case FunctionNode::Ctor:
|
|
writer.writeAttribute("meta", "constructor");
|
|
break;
|
|
case FunctionNode::Dtor:
|
|
writer.writeAttribute("meta", "destructor");
|
|
break;
|
|
case FunctionNode::MacroWithParams:
|
|
writer.writeAttribute("meta", "macrowithparams");
|
|
break;
|
|
case FunctionNode::MacroWithoutParams:
|
|
writer.writeAttribute("meta", "macrowithoutparams");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
writer.writeAttribute("const", functionNode->isConst()?"true":"false");
|
|
writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
|
|
writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
|
|
if (functionNode->isOverload())
|
|
writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
|
|
if (functionNode->relates())
|
|
writer.writeAttribute("relates", functionNode->relates()->name());
|
|
const PropertyNode *propertyNode = functionNode->associatedProperty();
|
|
if (propertyNode)
|
|
writer.writeAttribute("associated-property", propertyNode->name());
|
|
writer.writeAttribute("type", functionNode->returnType());
|
|
}
|
|
break;
|
|
|
|
case Node::QmlProperty:
|
|
{
|
|
const QmlPropertyNode *qpn = static_cast<const QmlPropertyNode*>(node);
|
|
writer.writeAttribute("type", qpn->dataType());
|
|
writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
|
|
writer.writeAttribute("writable", qpn->isWritable(this) ? "true" : "false");
|
|
}
|
|
break;
|
|
case Node::Property:
|
|
{
|
|
const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
|
|
writer.writeAttribute("type", propertyNode->dataType());
|
|
foreach (const Node *fnNode, propertyNode->getters()) {
|
|
if (fnNode) {
|
|
const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
|
|
writer.writeStartElement("getter");
|
|
writer.writeAttribute("name", functionNode->name());
|
|
writer.writeEndElement(); // getter
|
|
}
|
|
}
|
|
foreach (const Node *fnNode, propertyNode->setters()) {
|
|
if (fnNode) {
|
|
const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
|
|
writer.writeStartElement("setter");
|
|
writer.writeAttribute("name", functionNode->name());
|
|
writer.writeEndElement(); // getter
|
|
}
|
|
}
|
|
foreach (const Node *fnNode, propertyNode->resetters()) {
|
|
if (fnNode) {
|
|
const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode);
|
|
writer.writeStartElement("resetter");
|
|
writer.writeAttribute("name", functionNode->name());
|
|
writer.writeEndElement(); // getter
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Node::Variable:
|
|
{
|
|
const VariableNode *variableNode =
|
|
static_cast<const VariableNode*>(node);
|
|
writer.writeAttribute("type", variableNode->dataType());
|
|
writer.writeAttribute("static",
|
|
variableNode->isStatic() ? "true" : "false");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Inner nodes and function nodes contain child nodes of some sort, either
|
|
// actual child nodes or function parameters. For these, we close the
|
|
// opening tag, create child elements, then add a closing tag for the
|
|
// element. Elements for all other nodes are closed in the opening tag.
|
|
|
|
if (node->isInnerNode()) {
|
|
|
|
const InnerNode *inner = static_cast<const InnerNode*>(node);
|
|
|
|
// For internal pages, we canonicalize the target, keyword and content
|
|
// item names so that they can be used by qdoc for other sets of
|
|
// documentation.
|
|
// The reason we do this here is that we don't want to ruin
|
|
// externally composed indexes, containing non-qdoc-style target names
|
|
// when reading in indexes.
|
|
|
|
if (inner->doc().hasTargets()) {
|
|
bool external = false;
|
|
if (inner->type() == Node::Fake) {
|
|
const FakeNode *fakeNode = static_cast<const FakeNode *>(inner);
|
|
if (fakeNode->subType() == Node::ExternalPage)
|
|
external = true;
|
|
}
|
|
|
|
foreach (const Atom *target, inner->doc().targets()) {
|
|
QString targetName = target->string();
|
|
if (!external)
|
|
targetName = Doc::canonicalTitle(targetName);
|
|
|
|
writer.writeStartElement("target");
|
|
writer.writeAttribute("name", targetName);
|
|
writer.writeEndElement(); // target
|
|
}
|
|
}
|
|
if (inner->doc().hasKeywords()) {
|
|
foreach (const Atom *keyword, inner->doc().keywords()) {
|
|
writer.writeStartElement("keyword");
|
|
writer.writeAttribute("name",
|
|
Doc::canonicalTitle(keyword->string()));
|
|
writer.writeEndElement(); // keyword
|
|
}
|
|
}
|
|
if (inner->doc().hasTableOfContents()) {
|
|
for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
|
|
Atom *item = inner->doc().tableOfContents()[i];
|
|
int level = inner->doc().tableOfContentsLevels()[i];
|
|
|
|
QString title = Text::sectionHeading(item).toString();
|
|
writer.writeStartElement("contents");
|
|
writer.writeAttribute("name", Doc::canonicalTitle(title));
|
|
writer.writeAttribute("title", title);
|
|
writer.writeAttribute("level", QString::number(level));
|
|
writer.writeEndElement(); // contents
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (node->type() == Node::Function) {
|
|
|
|
const FunctionNode *functionNode = static_cast<const FunctionNode*>(node);
|
|
// Write a signature attribute for convenience.
|
|
QStringList signatureList;
|
|
QStringList resolvedParameters;
|
|
|
|
foreach (const Parameter ¶meter, functionNode->parameters()) {
|
|
QString leftType = parameter.leftType();
|
|
const Node *leftNode =
|
|
const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"),
|
|
Node::Typedef, 0, SearchBaseClasses|NonFunction);
|
|
if (!leftNode) {
|
|
leftNode = const_cast<Tree *>(this)->findNode(
|
|
parameter.leftType().split("::"), Node::Typedef,
|
|
node->parent(), SearchBaseClasses|NonFunction);
|
|
}
|
|
if (leftNode) {
|
|
if (leftNode->type() == Node::Typedef) {
|
|
const TypedefNode *typedefNode =
|
|
static_cast<const TypedefNode *>(leftNode);
|
|
if (typedefNode->associatedEnum()) {
|
|
leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
|
|
}
|
|
}
|
|
else
|
|
leftType = fullDocumentName(leftNode);
|
|
}
|
|
resolvedParameters.append(leftType);
|
|
signatureList.append(leftType + " " + parameter.name());
|
|
}
|
|
|
|
QString signature = functionNode->name()+"("+signatureList.join(", ")+")";
|
|
if (functionNode->isConst())
|
|
signature += " const";
|
|
writer.writeAttribute("signature", signature);
|
|
|
|
for (int i = 0; i < functionNode->parameters().size(); ++i) {
|
|
Parameter parameter = functionNode->parameters()[i];
|
|
writer.writeStartElement("parameter");
|
|
writer.writeAttribute("left", resolvedParameters[i]);
|
|
writer.writeAttribute("right", parameter.rightType());
|
|
writer.writeAttribute("name", parameter.name());
|
|
writer.writeAttribute("default", parameter.defaultValue());
|
|
writer.writeEndElement(); // parameter
|
|
}
|
|
|
|
}
|
|
else if (node->type() == Node::Enum) {
|
|
|
|
const EnumNode *enumNode = static_cast<const EnumNode*>(node);
|
|
if (enumNode->flagsType()) {
|
|
writer.writeAttribute("typedef",
|
|
fullDocumentName(enumNode->flagsType()));
|
|
}
|
|
foreach (const EnumItem &item, enumNode->items()) {
|
|
writer.writeStartElement("value");
|
|
writer.writeAttribute("name", item.name());
|
|
writer.writeAttribute("value", item.value());
|
|
writer.writeEndElement(); // value
|
|
}
|
|
|
|
}
|
|
else if (node->type() == Node::Typedef) {
|
|
|
|
const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
|
|
if (typedefNode->associatedEnum()) {
|
|
writer.writeAttribute("enum",
|
|
fullDocumentName(typedefNode->associatedEnum()));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*!
|
|
Returns true if the node \a n1 is less than node \a n2.
|
|
The comparison is performed by comparing properties of the nodes in order
|
|
of increasing complexity.
|
|
*/
|
|
bool compareNodes(const Node *n1, const Node *n2)
|
|
{
|
|
// Private nodes can occur in any order since they won't normally be
|
|
// written to the index.
|
|
if (n1->access() == Node::Private && n2->access() == Node::Private)
|
|
return true;
|
|
|
|
if (n1->location().filePath() < n2->location().filePath())
|
|
return true;
|
|
else if (n1->location().filePath() > n2->location().filePath())
|
|
return false;
|
|
|
|
if (n1->type() < n2->type())
|
|
return true;
|
|
else if (n1->type() > n2->type())
|
|
return false;
|
|
|
|
if (n1->name() < n2->name())
|
|
return true;
|
|
else if (n1->name() > n2->name())
|
|
return false;
|
|
|
|
if (n1->access() < n2->access())
|
|
return true;
|
|
else if (n1->access() > n2->access())
|
|
return false;
|
|
|
|
if (n1->type() == Node::Function && n2->type() == Node::Function) {
|
|
const FunctionNode *f1 = static_cast<const FunctionNode *>(n1);
|
|
const FunctionNode *f2 = static_cast<const FunctionNode *>(n2);
|
|
|
|
if (f1->isConst() < f2->isConst())
|
|
return true;
|
|
else if (f1->isConst() > f2->isConst())
|
|
return false;
|
|
|
|
if (f1->signature() < f2->signature())
|
|
return true;
|
|
else if (f1->signature() > f2->signature())
|
|
return false;
|
|
}
|
|
|
|
if (n1->type() == Node::Fake && n2->type() == Node::Fake) {
|
|
const FakeNode *f1 = static_cast<const FakeNode *>(n1);
|
|
const FakeNode *f2 = static_cast<const FakeNode *>(n2);
|
|
if (f1->fullTitle() < f2->fullTitle())
|
|
return true;
|
|
else if (f1->fullTitle() > f2->fullTitle())
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*!
|
|
Generate index sections for the child nodes of the given \a node
|
|
using the \a writer specified. If \a generateInternalNodes is true,
|
|
nodes marked as internal will be included in the index; otherwise,
|
|
they will be omitted.
|
|
*/
|
|
void Tree::generateIndexSections(QXmlStreamWriter &writer,
|
|
const Node *node,
|
|
bool generateInternalNodes) const
|
|
{
|
|
if (generateIndexSection(writer, node, generateInternalNodes)) {
|
|
|
|
if (node->isInnerNode()) {
|
|
const InnerNode *inner = static_cast<const InnerNode *>(node);
|
|
|
|
NodeList cnodes = inner->childNodes();
|
|
qSort(cnodes.begin(), cnodes.end(), compareNodes);
|
|
|
|
foreach (const Node *child, cnodes) {
|
|
/*
|
|
Don't generate anything for a QML property group node.
|
|
It is just a place holder for a collection of QML property
|
|
nodes. Recurse to its children, which are the QML property
|
|
nodes.
|
|
*/
|
|
if (child->subType() == Node::QmlPropertyGroup) {
|
|
const InnerNode *pgn = static_cast<const InnerNode*>(child);
|
|
foreach (const Node *c, pgn->childNodes()) {
|
|
generateIndexSections(writer, c, generateInternalNodes);
|
|
}
|
|
}
|
|
else
|
|
generateIndexSections(writer, child, generateInternalNodes);
|
|
}
|
|
|
|
/*
|
|
foreach (const Node *child, inner->relatedNodes()) {
|
|
QDomElement childElement = generateIndexSections(document, child);
|
|
element.appendChild(childElement);
|
|
}
|
|
*/
|
|
}
|
|
writer.writeEndElement();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Outputs an index file.
|
|
*/
|
|
void Tree::generateIndex(const QString &fileName,
|
|
const QString &url,
|
|
const QString &title,
|
|
bool generateInternalNodes) const
|
|
{
|
|
QFile file(fileName);
|
|
if (!file.open(QFile::WriteOnly | QFile::Text))
|
|
return ;
|
|
|
|
QXmlStreamWriter writer(&file);
|
|
writer.setAutoFormatting(true);
|
|
writer.writeStartDocument();
|
|
writer.writeDTD("<!DOCTYPE QDOCINDEX>");
|
|
|
|
writer.writeStartElement("INDEX");
|
|
writer.writeAttribute("url", url);
|
|
writer.writeAttribute("title", title);
|
|
writer.writeAttribute("version", version());
|
|
|
|
generateIndexSections(writer, root(), generateInternalNodes);
|
|
|
|
writer.writeEndElement(); // INDEX
|
|
writer.writeEndElement(); // QDOCINDEX
|
|
writer.writeEndDocument();
|
|
file.close();
|
|
}
|
|
|
|
/*!
|
|
Generate the tag file section with the given \a writer for the \a node
|
|
specified, returning true if an element was written; otherwise returns
|
|
false.
|
|
*/
|
|
void Tree::generateTagFileCompounds(QXmlStreamWriter &writer,
|
|
const InnerNode *inner) const
|
|
{
|
|
foreach (const Node *node, inner->childNodes()) {
|
|
|
|
if (!node->url().isEmpty())
|
|
continue;
|
|
|
|
QString kind;
|
|
switch (node->type()) {
|
|
case Node::Namespace:
|
|
kind = "namespace";
|
|
break;
|
|
case Node::Class:
|
|
kind = "class";
|
|
break;
|
|
case Node::Enum:
|
|
case Node::Typedef:
|
|
case Node::Property:
|
|
case Node::Function:
|
|
case Node::Variable:
|
|
case Node::Target:
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
QString access;
|
|
switch (node->access()) {
|
|
case Node::Public:
|
|
access = "public";
|
|
break;
|
|
case Node::Protected:
|
|
access = "protected";
|
|
break;
|
|
case Node::Private:
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
QString objName = node->name();
|
|
|
|
// Special case: only the root node should have an empty name.
|
|
if (objName.isEmpty() && node != root())
|
|
continue;
|
|
|
|
// *** Write the starting tag for the element here. ***
|
|
writer.writeStartElement("compound");
|
|
writer.writeAttribute("kind", kind);
|
|
|
|
if (node->type() == Node::Class) {
|
|
writer.writeTextElement("name", fullDocumentName(node));
|
|
writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node));
|
|
|
|
// Classes contain information about their base classes.
|
|
const ClassNode *classNode = static_cast<const ClassNode*>(node);
|
|
QList<RelatedClass> bases = classNode->baseClasses();
|
|
foreach (const RelatedClass &related, bases) {
|
|
ClassNode *baseClassNode = related.node;
|
|
writer.writeTextElement("base", baseClassNode->name());
|
|
}
|
|
|
|
// Recurse to write all members.
|
|
generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
|
|
writer.writeEndElement();
|
|
|
|
// Recurse to write all compounds.
|
|
generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
|
|
} else {
|
|
writer.writeTextElement("name", fullDocumentName(node));
|
|
writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node));
|
|
|
|
// Recurse to write all members.
|
|
generateTagFileMembers(writer, static_cast<const InnerNode *>(node));
|
|
writer.writeEndElement();
|
|
|
|
// Recurse to write all compounds.
|
|
generateTagFileCompounds(writer, static_cast<const InnerNode *>(node));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::generateTagFileMembers(QXmlStreamWriter &writer,
|
|
const InnerNode *inner) const
|
|
{
|
|
foreach (const Node *node, inner->childNodes()) {
|
|
|
|
if (!node->url().isEmpty())
|
|
continue;
|
|
|
|
QString nodeName;
|
|
QString kind;
|
|
switch (node->type()) {
|
|
case Node::Enum:
|
|
nodeName = "member";
|
|
kind = "enum";
|
|
break;
|
|
case Node::Typedef:
|
|
nodeName = "member";
|
|
kind = "typedef";
|
|
break;
|
|
case Node::Property:
|
|
nodeName = "member";
|
|
kind = "property";
|
|
break;
|
|
case Node::Function:
|
|
nodeName = "member";
|
|
kind = "function";
|
|
break;
|
|
case Node::Namespace:
|
|
nodeName = "namespace";
|
|
break;
|
|
case Node::Class:
|
|
nodeName = "class";
|
|
break;
|
|
case Node::Variable:
|
|
case Node::Target:
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
QString access;
|
|
switch (node->access()) {
|
|
case Node::Public:
|
|
access = "public";
|
|
break;
|
|
case Node::Protected:
|
|
access = "protected";
|
|
break;
|
|
case Node::Private:
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
QString objName = node->name();
|
|
|
|
// Special case: only the root node should have an empty name.
|
|
if (objName.isEmpty() && node != root())
|
|
continue;
|
|
|
|
// *** Write the starting tag for the element here. ***
|
|
writer.writeStartElement(nodeName);
|
|
if (!kind.isEmpty())
|
|
writer.writeAttribute("kind", kind);
|
|
|
|
switch (node->type()) {
|
|
|
|
case Node::Class:
|
|
writer.writeCharacters(fullDocumentName(node));
|
|
writer.writeEndElement();
|
|
break;
|
|
case Node::Namespace:
|
|
writer.writeCharacters(fullDocumentName(node));
|
|
writer.writeEndElement();
|
|
break;
|
|
case Node::Function:
|
|
{
|
|
/*
|
|
Function nodes contain information about
|
|
the type of function being described.
|
|
*/
|
|
|
|
const FunctionNode *functionNode =
|
|
static_cast<const FunctionNode*>(node);
|
|
writer.writeAttribute("protection", access);
|
|
|
|
switch (functionNode->virtualness()) {
|
|
case FunctionNode::NonVirtual:
|
|
writer.writeAttribute("virtualness", "non");
|
|
break;
|
|
case FunctionNode::ImpureVirtual:
|
|
writer.writeAttribute("virtualness", "virtual");
|
|
break;
|
|
case FunctionNode::PureVirtual:
|
|
writer.writeAttribute("virtual", "pure");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
writer.writeAttribute("static",
|
|
functionNode->isStatic() ? "yes" : "no");
|
|
|
|
if (functionNode->virtualness() == FunctionNode::NonVirtual)
|
|
writer.writeTextElement("type", functionNode->returnType());
|
|
else
|
|
writer.writeTextElement("type",
|
|
"virtual " + functionNode->returnType());
|
|
|
|
writer.writeTextElement("name", objName);
|
|
QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#");
|
|
writer.writeTextElement("anchorfile", pieces[0]);
|
|
writer.writeTextElement("anchor", pieces[1]);
|
|
|
|
// Write a signature attribute for convenience.
|
|
QStringList signatureList;
|
|
|
|
foreach (const Parameter ¶meter, functionNode->parameters()) {
|
|
QString leftType = parameter.leftType();
|
|
const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"),
|
|
Node::Typedef, 0, SearchBaseClasses|NonFunction);
|
|
if (!leftNode) {
|
|
leftNode = const_cast<Tree *>(this)->findNode(
|
|
parameter.leftType().split("::"), Node::Typedef,
|
|
node->parent(), SearchBaseClasses|NonFunction);
|
|
}
|
|
if (leftNode) {
|
|
const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode);
|
|
if (typedefNode->associatedEnum()) {
|
|
leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">";
|
|
}
|
|
}
|
|
signatureList.append(leftType + " " + parameter.name());
|
|
}
|
|
|
|
QString signature = "("+signatureList.join(", ")+")";
|
|
if (functionNode->isConst())
|
|
signature += " const";
|
|
if (functionNode->virtualness() == FunctionNode::PureVirtual)
|
|
signature += " = 0";
|
|
writer.writeTextElement("arglist", signature);
|
|
}
|
|
writer.writeEndElement(); // member
|
|
break;
|
|
|
|
case Node::Property:
|
|
{
|
|
const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node);
|
|
writer.writeAttribute("type", propertyNode->dataType());
|
|
writer.writeTextElement("name", objName);
|
|
QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#");
|
|
writer.writeTextElement("anchorfile", pieces[0]);
|
|
writer.writeTextElement("anchor", pieces[1]);
|
|
writer.writeTextElement("arglist", "");
|
|
}
|
|
writer.writeEndElement(); // member
|
|
break;
|
|
|
|
case Node::Enum:
|
|
{
|
|
const EnumNode *enumNode = static_cast<const EnumNode*>(node);
|
|
writer.writeTextElement("name", objName);
|
|
QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#");
|
|
writer.writeTextElement("anchor", pieces[1]);
|
|
writer.writeTextElement("arglist", "");
|
|
writer.writeEndElement(); // member
|
|
|
|
for (int i = 0; i < enumNode->items().size(); ++i) {
|
|
EnumItem item = enumNode->items().value(i);
|
|
writer.writeStartElement("member");
|
|
writer.writeAttribute("name", item.name());
|
|
writer.writeTextElement("anchor", pieces[1]);
|
|
writer.writeTextElement("arglist", "");
|
|
writer.writeEndElement(); // member
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Node::Typedef:
|
|
{
|
|
const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node);
|
|
if (typedefNode->associatedEnum())
|
|
writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum()));
|
|
else
|
|
writer.writeAttribute("type", "");
|
|
writer.writeTextElement("name", objName);
|
|
QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#");
|
|
writer.writeTextElement("anchorfile", pieces[0]);
|
|
writer.writeTextElement("anchor", pieces[1]);
|
|
writer.writeTextElement("arglist", "");
|
|
}
|
|
writer.writeEndElement(); // member
|
|
break;
|
|
|
|
case Node::Variable:
|
|
case Node::Target:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::generateTagFile(const QString &fileName) const
|
|
{
|
|
QFile file(fileName);
|
|
if (!file.open(QFile::WriteOnly | QFile::Text))
|
|
return ;
|
|
|
|
QXmlStreamWriter writer(&file);
|
|
writer.setAutoFormatting(true);
|
|
writer.writeStartDocument();
|
|
|
|
writer.writeStartElement("tagfile");
|
|
|
|
generateTagFileCompounds(writer, root());
|
|
|
|
writer.writeEndElement(); // tagfile
|
|
writer.writeEndDocument();
|
|
file.close();
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void Tree::addExternalLink(const QString &url, const Node *relative)
|
|
{
|
|
FakeNode *fakeNode = new FakeNode(root(), url, Node::ExternalPage);
|
|
fakeNode->setAccess(Node::Public);
|
|
|
|
// Create some content for the node.
|
|
QSet<QString> emptySet;
|
|
Location location(relative->doc().location());
|
|
Doc doc(location, location, " ", emptySet); // placeholder
|
|
fakeNode->setDoc(doc);
|
|
}
|
|
|
|
/*!
|
|
Construct the full document name for \a node and return the
|
|
name.
|
|
*/
|
|
QString Tree::fullDocumentName(const Node *node) const
|
|
{
|
|
if (!node)
|
|
return "";
|
|
|
|
QStringList pieces;
|
|
const Node *n = node;
|
|
|
|
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))
|
|
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.
|
|
if (n->type() == Node::Fake)
|
|
return pieces.join("#");
|
|
else
|
|
return pieces.join("::");
|
|
}
|
|
|
|
QT_END_NAMESPACE
|