/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "node.h"
#include "tree.h"
#include "codemarker.h"
#include "cppcodeparser.h"
#include <quuid.h>
#include "qdocdatabase.h"
#include <qdebug.h>
#include "generator.h"
#include "tokenizer.h"

QT_BEGIN_NAMESPACE

int Node::propertyGroupCount_ = 0;
QStringMap Node::operators_;
QMap<QString,Node::NodeType> Node::goals_;

/*!
  Initialize the map of search goals. This is called once
  by QDocDatabase::initializeDB(). The map key is a string
  representing a value in the enum Node::NodeType. The map value
  is the enum value.

  There should be an entry in the map for each value in the
  NodeType enum.
 */
void Node::initialize()
{
    goals_.insert("class", Node::Class);
    goals_.insert("qmltype", Node::QmlType);
    goals_.insert("page", Node::Document);
    goals_.insert("function", Node::Function);
    goals_.insert("property", Node::Property);
    goals_.insert("variable", Node::Variable);
    goals_.insert("group", Node::Group);
    goals_.insert("module", Node::Module);
    goals_.insert("qmlmodule", Node::QmlModule);
    goals_.insert("qmppropertygroup", Node::QmlPropertyGroup);
    goals_.insert("qmlproperty", Node::QmlProperty);
    goals_.insert("qmlsignal", Node::QmlSignal);
    goals_.insert("qmlsignalhandler", Node::QmlSignalHandler);
    goals_.insert("qmlmethod", Node::QmlMethod);
    goals_.insert("qmlbasictype", Node::QmlBasicType);
    goals_.insert("enum", Node::Enum);
    goals_.insert("typedef", Node::Typedef);
    goals_.insert("namespace", Node::Namespace);
}

/*!
  Increment the number of property groups seen in the current
  file, and return the new value.
 */
int Node::incPropertyGroupCount() { return ++propertyGroupCount_; }

/*!
  Reset the number of property groups seen in the current file
  to 0, because we are starting a new file.
 */
void Node::clearPropertyGroupCount() { propertyGroupCount_ = 0; }

/*!
  \class Node
  \brief The Node class is a node in the Tree.

  A Node represents a class or function or something else
  from the source code..
 */

/*!
  When this Node is destroyed, if it has a parent Node, it
  removes itself from the parent node's child list.
 */
Node::~Node()
{
    if (parent_)
        parent_->removeChild(this);

    if (relatesTo_)
        removeRelates();
}

/*!
    Removes this node from the aggregate's list of related
    nodes, or if this node has created a dummy "relates"
    aggregate, deletes it.
*/
void Node::removeRelates()
{
    if (!relatesTo_)
        return;

    if (relatesTo_->isDocumentNode() && !relatesTo_->parent()) {
        delete relatesTo_;
        relatesTo_ = 0;
    } else {
        relatesTo_->removeRelated(this);
    }
}

/*!
  Returns this node's name member. Appends "()" to the returned
  name, if this node is a function node.
 */
QString Node::plainName() const
{
    if (type() == Node::Function)
        return name_ + QLatin1String("()");
    return name_;
}

/*!
  Constructs and returns the node's fully qualified name by
  recursively ascending the parent links and prepending each
  parent name + "::". Breaks out when the parent pointer is
  \a relative. Almost all calls to this function pass 0 for
  \a relative.
 */
QString Node::plainFullName(const Node* relative) const
{
    if (name_.isEmpty())
        return QLatin1String("global");

    QString fullName;
    const Node* node = this;
    while (node) {
        fullName.prepend(node->plainName());
        if (node->parent() == relative || node->parent()->name().isEmpty())
            break;
        fullName.prepend(QLatin1String("::"));
        node = node->parent();
    }
    return fullName;
}

/*!
  Constructs and returns this node's full name.
 */
QString Node::fullName(const Node* relative) const
{
    if ((isDocumentNode() || isGroup()) && !title().isEmpty())
        return title();
    return plainFullName(relative);
}

/*!
  Try to match this node's type and subtype with one of the
  pairs in \a types. If a match is found, return true. If no
  match is found, return false.

  \a types is a list of type/subtype pairs, where the first
  value in the pair is a Node::NodeType, and the second value is
  a Node::DocSubtype. The second value is used in the match if
  this node's type is Node::Document.
 */
bool Node::match(const NodeTypeList& types) const
{
    for (int i=0; i<types.size(); ++i) {
        if (type() == types.at(i).first) {
            if (type() == Node::Document) {
                if (docSubtype() == types.at(i).second)
                    return true;
            }
            else
                return true;
        }
    }
    return false;
}

/*!
  Sets this Node's Doc to \a doc. If \a replace is false and
  this Node already has a Doc, a warning is reported that the
  Doc is being overridden, and it reports where the previous
  Doc was found. If \a replace is true, the Doc is replaced
  silently.
 */
void Node::setDoc(const Doc& doc, bool replace)
{
    if (!doc_.isEmpty() && !replace) {
        doc.location().warning(tr("Overrides a previous doc"));
        doc_.location().warning(tr("(The previous doc is here)"));
    }
    doc_ = doc;
}

/*!
  Construct a node with the given \a type and having the
  given \a parent and \a name. The new node is added to the
  parent's child list.
 */
Node::Node(NodeType type, Aggregate *parent, const QString& name)
    : nodeType_((unsigned char) type),
      access_((unsigned char) Public),
      safeness_((unsigned char) UnspecifiedSafeness),
      pageType_((unsigned char) NoPageType),
      status_((unsigned char) Active),
      indexNodeFlag_(false),
      parent_(parent),
      relatesTo_(0),
      name_(name)
{
    if (parent_)
        parent_->addChild(this);
    outSubDir_ = Generator::outputSubdir();
    if (operators_.isEmpty()) {
        operators_.insert("++","inc");
        operators_.insert("--","dec");
        operators_.insert("==","eq");
        operators_.insert("!=","ne");
        operators_.insert("<<","lt-lt");
        operators_.insert(">>","gt-gt");
        operators_.insert("+=","plus-assign");
        operators_.insert("-=","minus-assign");
        operators_.insert("*=","mult-assign");
        operators_.insert("/=","div-assign");
        operators_.insert("%=","mod-assign");
        operators_.insert("&=","bitwise-and-assign");
        operators_.insert("|=","bitwise-or-assign");
        operators_.insert("^=","bitwise-xor-assign");
        operators_.insert("<<=","bitwise-left-shift-assign");
        operators_.insert(">>=","bitwise-right-shift-assign");
        operators_.insert("||","logical-or");
        operators_.insert("&&","logical-and");
        operators_.insert("()","call");
        operators_.insert("[]","subscript");
        operators_.insert("->","pointer");
        operators_.insert("->*","pointer-star");
        operators_.insert("+","plus");
        operators_.insert("-","minus");
        operators_.insert("*","mult");
        operators_.insert("/","div");
        operators_.insert("%","mod");
        operators_.insert("|","bitwise-or");
        operators_.insert("&","bitwise-and");
        operators_.insert("^","bitwise-xor");
        operators_.insert("!","not");
        operators_.insert("~","bitwise-not");
        operators_.insert("<=","lt-eq");
        operators_.insert(">=","gt-eq");
        operators_.insert("<","lt");
        operators_.insert(">","gt");
        operators_.insert("=","assign");
        operators_.insert(",","comma");
        operators_.insert("delete[]","delete-array");
        operators_.insert("delete","delete");
        operators_.insert("new[]","new-array");
        operators_.insert("new","new");
    }
}

/*! \fn QString Node::url() const
  Returns the node's URL.
 */

/*! \fn void Node::setUrl(const QString &url)
  Sets the node's URL to \a url
 */

/*!
  Returns this node's page type as a string, for use as an
  attribute value in XML or HTML.
 */
QString Node::pageTypeString() const
{
    return pageTypeString((PageType) pageType_);
}

/*!
  Returns the page type \a t as a string, for use as an
  attribute value in XML or HTML.
 */
QString Node::pageTypeString(unsigned char t)
{
    switch ((PageType)t) {
    case Node::ApiPage:
        return "api";
    case Node::ArticlePage:
        return "article";
    case Node::ExamplePage:
        return "example";
    case Node::HowToPage:
        return "howto";
    case Node::OverviewPage:
        return "overview";
    case Node::TutorialPage:
        return "tutorial";
    case Node::FAQPage:
        return "faq";
    case Node::DitaMapPage:
        return "ditamap";
    default:
        return "article";
    }
}

/*!
  Returns this node's type as a string for use as an
  attribute value in XML or HTML.
 */
QString Node::nodeTypeString() const
{
    return nodeTypeString(type());
}

/*!
  Returns the node type \a t as a string for use as an
  attribute value in XML or HTML.
 */
QString Node::nodeTypeString(unsigned char t)
{
    switch ((NodeType)t) {
    case Namespace:
        return "namespace";
    case Class:
        return "class";
    case Document:
        return "document";
    case Enum:
        return "enum";
    case Typedef:
        return "typedef";
    case Function:
        return "function";
    case Property:
        return "property";
    case Variable:
        return "variable";
    case Group:
        return "group";
    case Module:
        return "module";
    case QmlType:
        return "QML type";
    case QmlBasicType:
        return "QML basic type";
    case QmlModule:
        return "QML module";
    case QmlProperty:
        return "QML property";
    case QmlPropertyGroup:
        return "QML property group";
    case QmlSignal:
        return "QML signal";
    case QmlSignalHandler:
        return "QML signal handler";
    case QmlMethod:
        return "QML method";
    default:
        break;
    }
    return QString();
}

/*!
  Returns this node's subtype as a string for use as an
  attribute value in XML or HTML. This is only useful
  in the case where the node type is Document.
 */
QString Node::nodeSubtypeString() const
{
    return nodeSubtypeString(docSubtype());
}

/*!
  Returns the node subtype \a t as a string for use as an
  attribute value in XML or HTML. This is only useful
  in the case where the node type is Document.
 */
QString Node::nodeSubtypeString(unsigned char t)
{
    switch ((DocSubtype)t) {
    case Example:
        return "example";
    case HeaderFile:
        return "header file";
    case File:
        return "file";
    case Image:
        return "image";
    case Page:
        return "page";
    case ExternalPage:
        return "external page";
    case DitaMap:
        return "ditamap";
    case NoSubtype:
    default:
        break;
    }
    return QString();
}

/*!
  Set the page type according to the string \a t.
 */
void Node::setPageType(const QString& t)
{
    if ((t == "API") || (t == "api"))
        pageType_ = (unsigned char) ApiPage;
    else if (t == "howto")
        pageType_ = (unsigned char) HowToPage;
    else if (t == "overview")
        pageType_ = (unsigned char) OverviewPage;
    else if (t == "tutorial")
        pageType_ = (unsigned char) TutorialPage;
    else if (t == "faq")
        pageType_ = (unsigned char) FAQPage;
    else if (t == "article")
        pageType_ = (unsigned char) ArticlePage;
    else if (t == "example")
        pageType_ = (unsigned char) ExamplePage;
    else if (t == "ditamap")
        pageType_ = (unsigned char) DitaMapPage;
}

/*! Converts the boolean value \a b to an enum representation
  of the boolean type, which includes an enum value for the
  \e {default value} of the item, i.e. true, false, or default.
 */
Node::FlagValue Node::toFlagValue(bool b)
{
    return b ? FlagValueTrue : FlagValueFalse;
}

/*!
  Converts the enum \a fv back to a boolean value.
  If \a fv is neither the true enum value nor the
  false enum value, the boolean value returned is
  \a defaultValue.

  Note that runtimeDesignabilityFunction() should be called
  first. If that function returns the name of a function, it
  means the function must be called at runtime to determine
  whether the property is Designable.
 */
bool Node::fromFlagValue(FlagValue fv, bool defaultValue)
{
    switch (fv) {
    case FlagValueTrue:
        return true;
    case FlagValueFalse:
        return false;
    default:
        return defaultValue;
    }
}

/*!
  Sets the pointer to the node that this node relates to.
 */
void Node::setRelates(Aggregate *pseudoParent)
{
    if (pseudoParent == parent())
        return;

    removeRelates();
    relatesTo_ = pseudoParent;
    pseudoParent->addRelated(this);
}

/*!
  Sets the (unresolved) entity \a name that this node relates to.
 */
void Node::setRelates(const QString& name)
{
    removeRelates();
    // Create a dummy aggregate for writing the name into the index
    relatesTo_ = new DocumentNode(0, name, Node::NoSubtype, Node::NoPageType);
}

/*!
  This function creates a pair that describes a link.
  The pair is composed from \a link and \a desc. The
  \a linkType is the map index the pair is filed under.
 */
void Node::setLink(LinkType linkType, const QString &link, const QString &desc)
{
    QPair<QString,QString> linkPair;
    linkPair.first = link;
    linkPair.second = desc;
    linkMap_[linkType] = linkPair;
}

/*!
    Sets the information about the project and version a node was introduced
    in. The string is simplified, removing excess whitespace before being
    stored.
*/
void Node::setSince(const QString &since)
{
    since_ = since.simplified();
}

/*!
  Returns a string representing the access specifier.
 */
QString Node::accessString() const
{
    switch ((Access) access_) {
    case Protected:
        return "protected";
    case Private:
        return "private";
    case Public:
    default:
        break;
    }
    return "public";
}

/*!
  Extract a class name from the type \a string and return it.
 */
QString Node::extractClassName(const QString &string) const
{
    QString result;
    for (int i=0; i<=string.size(); ++i) {
        QChar ch;
        if (i != string.size())
            ch = string.at(i);

        QChar lower = ch.toLower();
        if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) ||
                ch.digitValue() >= 0 ||
                ch == QLatin1Char('_') ||
                ch == QLatin1Char(':')) {
            result += ch;
        }
        else if (!result.isEmpty()) {
            if (result != QLatin1String("const"))
                return result;
            result.clear();
        }
    }
    return result;
}

/*!
  Returns a string representing the access specifier.
 */
QString RelatedClass::accessString() const
{
    switch (access_) {
    case Node::Protected:
        return "protected";
    case Node::Private:
        return "private";
    case Node::Public:
    default:
        break;
    }
    return "public";
}

/*!
  Returns the inheritance status.
 */
Node::Status Node::inheritedStatus() const
{
    Status parentStatus = Active;
    if (parent_)
        parentStatus = parent_->inheritedStatus();
    return (Status)qMin((int)status_, (int)parentStatus);
}

/*!
  Returns the thread safeness value for whatever this node
  represents. But if this node has a parent and the thread
  safeness value of the parent is the same as the thread
  safeness value of this node, what is returned is the
  value \c{UnspecifiedSafeness}. Why?
 */
Node::ThreadSafeness Node::threadSafeness() const
{
    if (parent_ && (ThreadSafeness) safeness_ == parent_->inheritedThreadSafeness())
        return UnspecifiedSafeness;
    return (ThreadSafeness) safeness_;
}

/*!
  If this node has a parent, the parent's thread safeness
  value is returned. Otherwise, this node's thread safeness
  value is returned. Why?
 */
Node::ThreadSafeness Node::inheritedThreadSafeness() const
{
    if (parent_ && (ThreadSafeness) safeness_ == UnspecifiedSafeness)
        return parent_->inheritedThreadSafeness();
    return (ThreadSafeness) safeness_;
}

#if 0
/*!
  Returns the sanitized file name without the path.
  If the file is an html file, the html suffix
  is removed. Why?
 */
QString Node::fileBase() const
{
    QString base = name();
    if (base.endsWith(".html"))
        base.chop(5);
    base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
    base = base.trimmed();
    base.replace(QLatin1Char(' '), QLatin1Char('-'));
    return base.toLower();
}
/*!
  Returns this node's Universally Unique IDentifier as a
  QString. Creates the UUID first, if it has not been created.
 */
QString Node::guid() const
{
    if (uuid_.isEmpty())
        uuid_ = idForNode();
    return uuid_;
}
#endif

/*!
  If this node is a QML or JS type node, return a pointer to
  it. If it is a child of a QML or JS type node, return the
  pointer to its parent QMLor JS type node. Otherwise return
  0;
 */
QmlTypeNode* Node::qmlTypeNode()
{
    if (isQmlNode() || isJsNode()) {
        Node* n = this;
        while (n && !(n->isQmlType() || n->isJsType()))
            n = n->parent();
        if (n && (n->isQmlType() || n->isJsType()))
            return static_cast<QmlTypeNode*>(n);
    }
    return 0;
}

/*!
  If this node is a QML node, find its QML class node,
  and return a pointer to the C++ class node from the
  QML class node. That pointer will be null if the QML
  class node is a component. It will be non-null if
  the QML class node is a QML element.
 */
ClassNode* Node::declarativeCppNode()
{
    QmlTypeNode* qcn = qmlTypeNode();
    if (qcn)
        return qcn->classNode();
    return 0;
}

/*!
  Returns \c true if the node's status is Internal, or if its
  parent is a class with internal status.
 */
bool Node::isInternal() const
{
    if (status() == Internal)
        return true;
    if (parent() && parent()->status() == Internal)
        return true;
    if (relates() && relates()->status() == Internal)
        return true;
    return false;
}

/*!
  Returns a pointer to the Tree this node is in.
 */
Tree* Node::tree() const
{
    return (parent() ? parent()->tree() : 0);
}

/*!
  Returns a pointer to the root of the Tree this node is in.
 */
const Node* Node::root() const
{
    return (parent() ? parent()->root() : this);
}

/*!
  Sets the node's declaration location, its definition
  location, or both, depending on the suffix of the file
  name from the file path in location \a t.
 */
void Node::setLocation(const Location& t)
{
    QString suffix = t.fileSuffix();
    if (suffix == "h")
        declLocation_ = t;
    else if (suffix == "cpp")
        defLocation_ = t;
    else {
        declLocation_ = t;
        defLocation_ = t;
    }
}

/*!
  \class Aggregate
 */

/*!
  The inner node destructor deletes the children and removes
  this node from its related nodes.
 */
Aggregate::~Aggregate()
{
    removeFromRelated();
    deleteChildren();
}

/*!
  If \a genus is \c{Node::DontCare}, find the first node in
  this node's child list that has the given \a name. If this
  node is a QML type, be sure to also look in the children
  of its property group nodes. Return the matching node or 0.

  If \a genus is either \c{Node::CPP} or \c {Node::QML}, then
  find all this node's children that have the given \a name,
  and return the one that satisfies the \a genus requirement.
 */
Node *Aggregate::findChildNode(const QString& name, Node::Genus genus) const
{
    if (genus == Node::DontCare) {
        Node *node = childMap_.value(name);
        if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group?
            return node;
        if (isQmlType() || isJsType()) {
            for (int i=0; i<children_.size(); ++i) {
                Node* n = children_.at(i);
                if (n->isQmlPropertyGroup() || isJsPropertyGroup()) {
                    node = static_cast<Aggregate*>(n)->findChildNode(name, genus);
                    if (node)
                        return node;
                }
            }
        }
    }
    else {
        NodeList nodes = childMap_.values(name);
        if (!nodes.isEmpty()) {
            for (int i=0; i<nodes.size(); ++i) {
                Node* node = nodes.at(i);
                if (genus == node->genus())
                    return node;
            }
        }
    }
    return primaryFunctionMap_.value(name);
}

/*!
  Find all the child nodes of this node that are named
  \a name and return them in \a nodes.
 */
void Aggregate::findChildren(const QString& name, NodeList& nodes) const
{
    nodes = childMap_.values(name);
    Node* n = primaryFunctionMap_.value(name);
    if (n) {
        nodes.append(n);
        NodeList t = secondaryFunctionMap_.value(name);
        if (!t.isEmpty())
            nodes.append(t);
    }
    if (!nodes.isEmpty() || !(isQmlNode() || isJsNode()))
        return;
    int i = name.indexOf(QChar('.'));
    if (i < 0)
        return;
    QString qmlPropGroup = name.left(i);
    NodeList t = childMap_.values(qmlPropGroup);
    if (t.isEmpty())
        return;
    foreach (Node* n, t) {
        if (n->isQmlPropertyGroup() || n->isJsPropertyGroup()) {
            n->findChildren(name, nodes);
            if (!nodes.isEmpty())
                break;
        }
    }
}

/*!
  This function is like findChildNode(), but if a node
  with the specified \a name is found but it is not of the
  specified \a type, 0 is returned.
 */
Node* Aggregate::findChildNode(const QString& name, NodeType type)
{
    if (type == Function)
        return primaryFunctionMap_.value(name);
    else {
        NodeList nodes = childMap_.values(name);
        for (int i=0; i<nodes.size(); ++i) {
            Node* node = nodes.at(i);
            if (node->type() == type)
                return node;
        }
    }
    return 0;
}

/*!
  Find a function node that is a child of this nose, such
  that the function node has the specified \a name.
 */
FunctionNode *Aggregate::findFunctionNode(const QString& name, const QString& params) const
{
    FunctionNode* pfn = static_cast<FunctionNode*>(primaryFunctionMap_.value(name));
    FunctionNode* fn = pfn;
    if (fn) {
        const QVector<Parameter>* funcParams = &(fn->parameters());
        if (params.isEmpty() && funcParams->isEmpty() && !fn->isInternal())
            return fn;
        bool isQPrivateSignal = false; // Not used in the search
        QVector<Parameter> testParams;
        if (!params.isEmpty()) {
            CppCodeParser* cppParser = CppCodeParser::cppParser();
            cppParser->parseParameters(params, testParams, isQPrivateSignal);
        }
        NodeList funcs = secondaryFunctionMap_.value(name);
        int i = -1;
        while (fn) {
            if (testParams.size() == funcParams->size()) {
                if (testParams.isEmpty() && !fn->isInternal())
                    return fn;
                bool different = false;
                for (int j=0; j<testParams.size(); j++) {
                    if (testParams.at(j).dataType() != funcParams->at(j).dataType()) {
                        different = true;
                        break;
                    }
                }
                if (!different && !fn->isInternal())
                    return fn;
            }
            if (++i < funcs.size()) {
                fn = static_cast<FunctionNode*>(funcs.at(i));
                funcParams = &(fn->parameters());
            }
            else
                fn = 0;
        }
        /*
          Most \l commands that link to functions don't include
          the parameter declarations in the function signature,
          so if the \l is meant to go to a function that does
          have parameters, the algorithm above won't find it.
          Therefore we must return the pointer to the function
          in the primary function map in the cases where the
          parameters should have been specified in the \l command.
          But if the primary function is marked internal, search
          the secondary list to find one that is not marked internal.
        */
        if (!fn) {
            if (!testParams.empty())
                return 0;
            if (pfn && !pfn->isInternal())
                return pfn;
            foreach (Node* n, funcs) {
                fn = static_cast<FunctionNode*>(n);
                if (!fn->isInternal())
                    return fn;
            }
        }
    }
    return fn;
}

/*!
  Find the function node that is a child of this node, such
  that the function has the same name and signature as the
  \a clone node.
 */
FunctionNode *Aggregate::findFunctionNode(const FunctionNode *clone) const
{
    QMap<QString,Node*>::ConstIterator c = primaryFunctionMap_.constFind(clone->name());
    if (c != primaryFunctionMap_.constEnd()) {
        if (isSameSignature(clone, (FunctionNode *) *c)) {
            return (FunctionNode *) *c;
        }
        else if (secondaryFunctionMap_.contains(clone->name())) {
            const NodeList& secs = secondaryFunctionMap_[clone->name()];
            NodeList::ConstIterator s = secs.constBegin();
            while (s != secs.constEnd()) {
                if (isSameSignature(clone, (FunctionNode *) *s))
                    return (FunctionNode *) *s;
                ++s;
            }
        }
    }
    return 0;
}

/*!
  Returns the list of keys from the primary function map.
 */
QStringList Aggregate::primaryKeys()
{
    QStringList t;
    QMap<QString, Node*>::iterator i = primaryFunctionMap_.begin();
    while (i != primaryFunctionMap_.end()) {
        t.append(i.key());
        ++i;
    }
    return t;
}

/*!
  Returns the list of keys from the secondary function map.
 */
QStringList Aggregate::secondaryKeys()
{
    QStringList t;
    QMap<QString, NodeList>::iterator i = secondaryFunctionMap_.begin();
    while (i != secondaryFunctionMap_.end()) {
        t.append(i.key());
        ++i;
    }
    return t;
}

/*!
  Mark all child nodes that have no documentation as having
  private access and internal status. qdoc will then ignore
  them for documentation purposes. Some nodes have an
  Intermediate status, meaning that they should be ignored,
  but not their children.
 */
void Aggregate::makeUndocumentedChildrenInternal()
{
    foreach (Node *child, childNodes()) {
        if (child->doc().isEmpty() && child->status() != Node::Intermediate) {
            child->setAccess(Node::Private);
            child->setStatus(Node::Internal);
        }
    }
}

/*!
  This is where we set the overload numbers for function nodes.
  \note Overload numbers for related non-members are handled
  separately.
 */
void Aggregate::normalizeOverloads()
{
    QMap<QString, Node *>::Iterator p1 = primaryFunctionMap_.begin();
    while (p1 != primaryFunctionMap_.end()) {
        FunctionNode *primaryFunc = (FunctionNode *) *p1;
        if (primaryFunc->status() != Active || primaryFunc->access() == Private) {
            if (secondaryFunctionMap_.contains(primaryFunc->name())) {
                /*
                  Either the primary function is not active or it is private.
                  It therefore can't be the primary function. Search the list
                  of overloads to find one that can be the primary function.
                */
                NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()];
                NodeList::ConstIterator s = overloads.constBegin();
                while (s != overloads.constEnd()) {
                    FunctionNode *overloadFunc = (FunctionNode *) *s;
                    /*
                      Any non-obsolete, non-private function (i.e., visible function)
                      is preferable to the current primary function. Swap the primary
                      and overload functions.
                    */
                    if (overloadFunc->status() == Active && overloadFunc->access() != Private) {
                        primaryFunc->setOverloadNumber(overloadFunc->overloadNumber());
                        overloads.replace(overloads.indexOf(overloadFunc), primaryFunc);
                        *p1 = overloadFunc;
                        overloadFunc->setOverloadFlag(false);
                        overloadFunc->setOverloadNumber(0);
                        break;
                    }
                    ++s;
                }
            }
        }
        ++p1;
    }
    /*
      Ensure that none of the primary functions is marked with \overload.
     */
    QMap<QString, Node *>::Iterator p = primaryFunctionMap_.begin();
    while (p != primaryFunctionMap_.end()) {
        FunctionNode *primaryFunc = (FunctionNode *) *p;
        if (primaryFunc->isOverload()) {
            if (secondaryFunctionMap_.contains(primaryFunc->name())) {
                /*
                  The primary function is marked with \overload. Find an
                  overload in the secondary function map that is not marked
                  with \overload but that is active and not private. Then
                  swap it with the primary function.
                */
                NodeList& overloads = secondaryFunctionMap_[primaryFunc->name()];
                NodeList::ConstIterator s = overloads.constBegin();
                while (s != overloads.constEnd()) {
                    FunctionNode *overloadFunc = (FunctionNode *) *s;
                    if (!overloadFunc->isOverload()) {
                        if (overloadFunc->status() == Active && overloadFunc->access() != Private) {
                            primaryFunc->setOverloadNumber(overloadFunc->overloadNumber());
                            overloads.replace(overloads.indexOf(overloadFunc), primaryFunc);
                            *p = overloadFunc;
                            overloadFunc->setOverloadFlag(false);
                            overloadFunc->setOverloadNumber(0);
                            break;
                        }
                    }
                    ++s;
                }
            }
        }
        ++p;
    }
    /*
      Recursive part.
     */
    NodeList::ConstIterator c = childNodes().constBegin();
    while (c != childNodes().constEnd()) {
        if ((*c)->isAggregate())
            ((Aggregate *) *c)->normalizeOverloads();
        ++c;
    }
}

/*!
 */
void Aggregate::removeFromRelated()
{
    while (!related_.isEmpty()) {
        Node *p = static_cast<Node *>(related_.takeFirst());

        if (p != 0 && p->relates() == this) p->clearRelated();
    }
}

/*!
  Deletes all this node's children.
 */
void Aggregate::deleteChildren()
{
    NodeList childrenCopy = children_;
    // Clear internal collections before deleting child nodes
    children_.clear();
    childMap_.clear();
    enumChildren_.clear();
    primaryFunctionMap_.clear();
    secondaryFunctionMap_.clear();
    qDeleteAll(childrenCopy);
}

/*! \fn bool Aggregate::isAggregate() const
  Returns \c true because this is an inner node.
 */

/*!
  Returns \c true if the node is a class node or a QML type node
  that is marked as being a wrapper class or QML type, or if
  it is a member of a wrapper class or type.
 */
bool Node::isWrapper() const
{
    return (parent_ ? parent_->isWrapper() : false);
}

/*!
  Finds the enum type node that has \a enumValue as one of
  its enum values and returns a pointer to it. Returns 0 if
  no enum type node is found that has \a enumValue as one
  of its values.
 */
const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
{
    foreach (const Node *node, enumChildren_) {
        const EnumNode *en = static_cast<const EnumNode *>(node);
        if (en->hasItem(enumValue))
            return en;
    }
    return 0;
}

/*!
  Returns a node list containing all the member functions of
  some class such that the functions overload the name \a funcName.
 */
NodeList Aggregate::overloads(const QString &funcName) const
{
    NodeList result;
    Node *primary = primaryFunctionMap_.value(funcName);
    if (primary) {
        result << primary;
        result += secondaryFunctionMap_[funcName];
    }
    return result;
}

/*!
  Construct an inner node (i.e., not a leaf node) of the
  given \a type and having the given \a parent and \a name.
 */
Aggregate::Aggregate(NodeType type, Aggregate *parent, const QString& name)
    : Node(type, parent, name)
{
    switch (type) {
    case Class:
    case QmlType:
    case Namespace:
        setPageType(ApiPage);
        break;
    default:
        break;
    }
}

/*!
  Appends an \a include file to the list of include files.
 */
void Aggregate::addInclude(const QString& include)
{
    includes_.append(include);
}

/*!
  Sets the list of include files to \a includes.
 */
void Aggregate::setIncludes(const QStringList& includes)
{
    includes_ = includes;
}

/*!
  f1 is always the clone
 */
bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
{
    if (f1->parameters().size() != f2->parameters().size())
        return false;
    if (f1->isConst() != f2->isConst())
        return false;

    QVector<Parameter>::ConstIterator p1 = f1->parameters().constBegin();
    QVector<Parameter>::ConstIterator p2 = f2->parameters().constBegin();
    while (p2 != f2->parameters().constEnd()) {
        if ((*p1).hasType() && (*p2).hasType()) {
            if ((*p1).rightType() != (*p2).rightType())
                return false;

            QString t1 = p1->dataType();
            QString t2 = p2->dataType();

            if (t1.length() < t2.length())
                qSwap(t1, t2);

            /*
              ### hack for C++ to handle superfluous
              "Foo::" prefixes gracefully
            */
            if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2))
                return false;
        }
        ++p1;
        ++p2;
    }
    return true;
}

/*!
  Adds the \a child to this node's child list. It might also
  be necessary to update this node's internal collections and
  the child's parent pointer and output subdirectory.
 */
void Aggregate::addChild(Node *child)
{
    children_.append(child);
    if (child->type() == Function
            || child->type() == QmlMethod
            || child->type() == QmlSignal) {
        FunctionNode *func = static_cast<FunctionNode*>(child);
        QString name = func->name();
        if (!primaryFunctionMap_.contains(name)) {
            primaryFunctionMap_.insert(name, func);
            func->setOverloadNumber(0);
        }
        else {
            NodeList &overloads = secondaryFunctionMap_[name];
            overloads.append(func);
            func->setOverloadNumber(overloads.size());
        }
    }
    else {
        if (child->type() == Enum)
            enumChildren_.append(child);
        childMap_.insertMulti(child->name(), child);
    }
    if (child->parent() == 0) {
        child->setParent(this);
        child->setOutputSubdirectory(this->outputSubdirectory());
    }
}

/*!
  Adds the \a child to this node's child map using \a title
  as the key. The \a child is not added to the child list
  again, because it is presumed to already be there. We just
  want to be able to find the child by its \a title.
 */
void Aggregate::addChild(Node* child, const QString& title)
{
    childMap_.insertMulti(title, child);
}

/*!
  The \a child is removed from this node's child list and
  from this node's internal collections. The child's parent
  pointer is set to 0, but its output subdirectory is not
  changed.
 */
void Aggregate::removeChild(Node *child)
{
    children_.removeAll(child);
    enumChildren_.removeAll(child);
    if (child->type() == Function
            || child->type() == QmlMethod
            || child->type() == QmlSignal) {
        QMap<QString, Node *>::Iterator primary = primaryFunctionMap_.find(child->name());
        NodeList& overloads = secondaryFunctionMap_[child->name()];
        if (primary != primaryFunctionMap_.end() && *primary == child) {
            primaryFunctionMap_.erase(primary);
            if (!overloads.isEmpty()) {
                FunctionNode* fn = static_cast<FunctionNode*>(overloads.takeFirst());
                fn->setOverloadNumber(0);
                primaryFunctionMap_.insert(child->name(), fn);
            }
        }
        else
            overloads.removeAll(child);
    }
    QMap<QString, Node *>::Iterator ent = childMap_.find(child->name());
    while (ent != childMap_.end() && ent.key() == child->name()) {
        if (*ent == child) {
            childMap_.erase(ent);
            break;
        }
        ++ent;
    }
    if (child->title().isEmpty())
        return;
    ent = childMap_.find(child->title());
    while (ent != childMap_.end() && ent.key() == child->title()) {
        if (*ent == child) {
            childMap_.erase(ent);
            break;
        }
        ++ent;
    }
    child->setParent(0);
}

/*!
 Recursively sets the output subdirectory for children
 */
void Aggregate::setOutputSubdirectory(const QString &t)
{
    Node::setOutputSubdirectory(t);
    for (int i = 0; i < childNodes().size(); ++i)
        childNodes().at(i)->setOutputSubdirectory(t);
}

/*!
  Find the module (Qt Core, Qt GUI, etc.) to which the class belongs.
  We do this by obtaining the full path to the header file's location
  and examine everything between "src/" and the filename.  This is
  semi-dirty because we are assuming a particular directory structure.

  This function is only really useful if the class's module has not
  been defined in the header file with a QT_MODULE macro or with an
  \inmodule command in the documentation.
*/
QString Node::physicalModuleName() const
{
    if (!physicalModuleName_.isEmpty())
        return physicalModuleName_;

    QString path = location().filePath();
    QString pattern = QString("src") + QDir::separator();
    int start = path.lastIndexOf(pattern);

    if (start == -1)
        return QString();

    QString moduleDir = path.mid(start + pattern.size());
    int finish = moduleDir.indexOf(QDir::separator());

    if (finish == -1)
        return QString();

    QString physicalModuleName = moduleDir.left(finish);

    if (physicalModuleName == "corelib")
        return "QtCore";
    else if (physicalModuleName == "uitools")
        return "QtUiTools";
    else if (physicalModuleName == "gui")
        return "QtGui";
    else if (physicalModuleName == "network")
        return "QtNetwork";
    else if (physicalModuleName == "opengl")
        return "QtOpenGL";
    else if (physicalModuleName == "svg")
        return "QtSvg";
    else if (physicalModuleName == "sql")
        return "QtSql";
    else if (physicalModuleName == "qtestlib")
        return "QtTest";
    else if (moduleDir.contains("webkit"))
        return "QtWebKit";
    else if (physicalModuleName == "xml")
        return "QtXml";
    else
        return QString();
}

/*!
  Removes a node from the list of nodes related to this one.
  If it is a function node, also remove from the primary/
  secondary function maps.
 */
void Aggregate::removeRelated(Node *pseudoChild)
{
    related_.removeAll(pseudoChild);

    if (pseudoChild->isFunction()) {
        QMap<QString, Node *>::Iterator p = primaryFunctionMap_.find(pseudoChild->name());
        while (p != primaryFunctionMap_.end()) {
            if (p.value() == pseudoChild) {
                primaryFunctionMap_.erase(p);
                break;
            }
            ++p;
        }
        NodeList& overloads = secondaryFunctionMap_[pseudoChild->name()];
        overloads.removeAll(pseudoChild);
    }
}

/*!
  Adds \a pseudoChild to the list of nodes related to this one. Resolve a correct
  overload number for a related non-member function.
 */
void Aggregate::addRelated(Node *pseudoChild)
{
    related_.append(pseudoChild);

    if (pseudoChild->isFunction()) {
        FunctionNode* fn = static_cast<FunctionNode*>(pseudoChild);
        if (primaryFunctionMap_.contains(pseudoChild->name())) {
            secondaryFunctionMap_[pseudoChild->name()].append(pseudoChild);
            fn->setOverloadNumber(secondaryFunctionMap_[pseudoChild->name()].size());
            fn->setOverloadFlag(true);
        }
        else {
            primaryFunctionMap_.insert(pseudoChild->name(), pseudoChild);
            fn->setOverloadNumber(0);
            fn->setOverloadFlag(false);
        }
    }
}

/*!
  If this node has a child that is a QML property named \a n,
  return the pointer to that child.
 */
QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n) const
{
    foreach (Node* child, childNodes()) {
        if (child->type() == Node::QmlProperty) {
            if (child->name() == n)
                return static_cast<QmlPropertyNode*>(child);
        }
        else if (child->isQmlPropertyGroup()) {
            QmlPropertyNode* t = child->hasQmlProperty(n);
            if (t)
                return t;
        }
    }
    return 0;
}

/*!
  If this node has a child that is a QML property named \a n
  whose type (attached or normal property) matches \a attached,
  return the pointer to that child.
 */
QmlPropertyNode* Aggregate::hasQmlProperty(const QString& n, bool attached) const
{
    foreach (Node* child, childNodes()) {
        if (child->type() == Node::QmlProperty) {
            if (child->name() == n && child->isAttached() == attached)
                return static_cast<QmlPropertyNode*>(child);
        }
        else if (child->isQmlPropertyGroup()) {
            QmlPropertyNode* t = child->hasQmlProperty(n, attached);
            if (t)
                return t;
        }
    }
    return 0;
}

/*!
  \class LeafNode
 */

/*! \fn bool LeafNode::isAggregate() const
  Returns \c false because this is a LeafNode.
 */

/*!
  Constructs a leaf node named \a name of the specified
  \a type. The new leaf node becomes a child of \a parent.
 */
LeafNode::LeafNode(NodeType type, Aggregate *parent, const QString& name)
    : Node(type, parent, name)
{
    switch (type) {
    case Enum:
    case Function:
    case Typedef:
    case Variable:
    case QmlProperty:
    case QmlSignal:
    case QmlSignalHandler:
    case QmlMethod:
    case QmlBasicType:
        setPageType(ApiPage);
        break;
    default:
        break;
    }
}

/*!
  This constructor should only be used when this node's parent
  is meant to be \a parent, but this node is not to be listed
  as a child of \a parent. It is currently only used for the
  documentation case where a \e{qmlproperty} command is used
  to override the QML definition of a QML property.
 */
LeafNode::LeafNode(Aggregate* parent, NodeType type, const QString& name)
    : Node(type, 0, name)
{
    setParent(parent);
    switch (type) {
    case Enum:
    case Function:
    case Typedef:
    case Variable:
    case QmlProperty:
    case QmlSignal:
    case QmlSignalHandler:
    case QmlMethod:
        setPageType(ApiPage);
        break;
    default:
        break;
    }
}


/*!
  \class NamespaceNode
 */

/*!
  Constructs a namespace node.
 */
NamespaceNode::NamespaceNode(Aggregate *parent, const QString& name)
    : Aggregate(Namespace, parent, name), seen_(false), tree_(0)
{
    setGenus(Node::CPP);
    setPageType(ApiPage);
}

/*!
  \class ClassNode
  \brief This class represents a C++ class.
 */

/*!
  Constructs a class node. A class node will generate an API page.
 */
ClassNode::ClassNode(Aggregate *parent, const QString& name)
    : Aggregate(Class, parent, name)
{
    abstract_ = false;
    wrapper_ = false;
    qmlelement = 0;
    setGenus(Node::CPP);
    setPageType(ApiPage);
}

/*!
  Adds the base class \a node to this class's list of base
  classes. The base class has the specified \a access. This
  is a resolved base class.
 */
void ClassNode::addResolvedBaseClass(Access access, ClassNode* node)
{
    bases_.append(RelatedClass(access, node));
    node->derived_.append(RelatedClass(access, this));
}

/*!
  Adds the derived class \a node to this class's list of derived
  classes. The derived class inherits this class with \a access.
 */
void ClassNode::addDerivedClass(Access access, ClassNode* node)
{
    derived_.append(RelatedClass(access, node));
}

/*!
  Add an unresolved base class to this class node's list of
  base classes. The unresolved base class will be resolved
  before the generate phase of qdoc. In an unresolved base
  class, the pointer to the base class node is 0.
 */
void ClassNode::addUnresolvedBaseClass(Access access,
                                       const QStringList& path,
                                       const QString& signature)
{
    bases_.append(RelatedClass(access, path, signature));
}

/*!
  Add an unresolved \c using clause to this class node's list
  of \c using clauses. The unresolved \c using clause will be
  resolved before the generate phase of qdoc. In an unresolved
  \c using clause, the pointer to the function node is 0.
 */
void ClassNode::addUnresolvedUsingClause(const QString& signature)
{
    usingClauses_.append(UsingClause(signature));
}

/*!
 */
void ClassNode::fixBaseClasses()
{
    int i;
    i = 0;
    QSet<ClassNode *> found;

    // Remove private and duplicate base classes.
    while (i < bases_.size()) {
        ClassNode* bc = bases_.at(i).node_;
        if (bc && (bc->access() == Node::Private || found.contains(bc))) {
            RelatedClass rc = bases_.at(i);
            bases_.removeAt(i);
            ignoredBases_.append(rc);
            const QList<RelatedClass> &bb = bc->baseClasses();
            for (int j = bb.size() - 1; j >= 0; --j)
                bases_.insert(i, bb.at(j));
        }
        else {
            ++i;
        }
        found.insert(bc);
    }

    i = 0;
    while (i < derived_.size()) {
        ClassNode* dc = derived_.at(i).node_;
        if (dc && dc->access() == Node::Private) {
            derived_.removeAt(i);
            const QList<RelatedClass> &dd = dc->derivedClasses();
            for (int j = dd.size() - 1; j >= 0; --j)
                derived_.insert(i, dd.at(j));
        }
        else {
            ++i;
        }
    }
}

/*!
  Not sure why this is needed.
 */
void ClassNode::fixPropertyUsingBaseClasses(PropertyNode* pn)
{
    QList<RelatedClass>::const_iterator bc = baseClasses().constBegin();
    while (bc != baseClasses().constEnd()) {
        ClassNode* cn = bc->node_;
        if (cn) {
            Node* n = cn->findChildNode(pn->name(), Node::Property);
            if (n) {
                PropertyNode* baseProperty = static_cast<PropertyNode*>(n);
                cn->fixPropertyUsingBaseClasses(baseProperty);
                pn->setOverriddenFrom(baseProperty);
            }
            else
                cn->fixPropertyUsingBaseClasses(pn);
        }
        ++bc;
    }
}

/*!
  Search the child list to find the property node with the
  specified \a name.
 */
PropertyNode* ClassNode::findPropertyNode(const QString& name)
{
    Node* n = findChildNode(name, Node::Property);

    if (n)
        return static_cast<PropertyNode*>(n);

    PropertyNode* pn = 0;

    const QList<RelatedClass> &bases = baseClasses();
    if (!bases.isEmpty()) {
        for (int i = 0; i < bases.size(); ++i) {
            ClassNode* cn = bases[i].node_;
            if (cn) {
                pn = cn->findPropertyNode(name);
                if (pn)
                    break;
            }
        }
    }
    const QList<RelatedClass>& ignoredBases = ignoredBaseClasses();
    if (!ignoredBases.isEmpty()) {
        for (int i = 0; i < ignoredBases.size(); ++i) {
            ClassNode* cn = ignoredBases[i].node_;
            if (cn) {
                pn = cn->findPropertyNode(name);
                if (pn)
                    break;
            }
        }
    }

    return pn;
}

/*!
  This function does a recursive search of this class node's
  base classes looking for one that has a QML element. If it
  finds one, it returns the pointer to that QML element. If
  it doesn't find one, it returns null.
 */
QmlTypeNode* ClassNode::findQmlBaseNode()
{
    QmlTypeNode* result = 0;
    const QList<RelatedClass>& bases = baseClasses();

    if (!bases.isEmpty()) {
        for (int i = 0; i < bases.size(); ++i) {
            ClassNode* cn = bases[i].node_;
            if (cn && cn->qmlElement()) {
                return cn->qmlElement();
            }
        }
        for (int i = 0; i < bases.size(); ++i) {
            ClassNode* cn = bases[i].node_;
            if (cn) {
                result = cn->findQmlBaseNode();
                if (result != 0) {
                    return result;
                }
            }
        }
    }
    return result;
}

/*!
  \class DocumentNode
 */

/*!
  The type of a DocumentNode is Document, and it has a \a subtype,
  which specifies the type of DocumentNode. The page type for
  the page index is set here.
 */
DocumentNode::DocumentNode(Aggregate* parent, const QString& name, DocSubtype subtype, Node::PageType ptype)
    : Aggregate(Document, parent, name), nodeSubtype_(subtype)
{
    setGenus(Node::DOC);
    switch (subtype) {
    case Page:
        setPageType(ptype);
        break;
    case DitaMap:
        setPageType(ptype);
        break;
    case Example:
        setPageType(ExamplePage);
        break;
    default:
        break;
    }
}

/*! \fn QString DocumentNode::title() const
  Returns the document node's title. This is used for the page title.
*/

/*!
  Sets the document node's \a title. This is used for the page title.
 */
void DocumentNode::setTitle(const QString &title)
{
    title_ = title;
    parent()->addChild(this, title);
}

/*!
  Returns the document node's full title, which is usually
  just title(), but for some DocSubtype values is different
  from title()
 */
QString DocumentNode::fullTitle() const
{
    if (nodeSubtype_ == File) {
        if (title().isEmpty())
            return name().mid(name().lastIndexOf('/') + 1) + " Example File";
        else
            return title();
    }
    else if (nodeSubtype_ == Image) {
        if (title().isEmpty())
            return name().mid(name().lastIndexOf('/') + 1) + " Image File";
        else
            return title();
    }
    else if (nodeSubtype_ == HeaderFile) {
        if (title().isEmpty())
            return name();
        else
            return name() + " - " + title();
    }
    else {
        return title();
    }
}

/*!
  Returns the subtitle.
 */
QString DocumentNode::subTitle() const
{
    if (!subtitle_.isEmpty())
        return subtitle_;

    if ((nodeSubtype_ == File) || (nodeSubtype_ == Image)) {
        if (title().isEmpty() && name().contains(QLatin1Char('/')))
            return name();
    }
    return QString();
}

/*!
  \class EnumNode
 */

/*!
  The constructor for the node representing an enum type
  has a \a parent class and an enum type \a name.
 */
EnumNode::EnumNode(Aggregate *parent, const QString& name)
    : LeafNode(Enum, parent, name), flagsType_(0)
{
    setGenus(Node::CPP);
}

/*!
  Add \a item to the enum type's item list.
 */
void EnumNode::addItem(const EnumItem& item)
{
    items_.append(item);
    names_.insert(item.name());
}

/*!
  Returns the access level of the enumeration item named \a name.
  Apparently it is private if it has been omitted by qdoc's
  omitvalue command. Otherwise it is public.
 */
Node::Access EnumNode::itemAccess(const QString &name) const
{
    if (doc().omitEnumItemNames().contains(name))
        return Private;
    return Public;
}

/*!
  Returns the enum value associated with the enum \a name.
 */
QString EnumNode::itemValue(const QString &name) const
{
    foreach (const EnumItem &item, items_) {
        if (item.name() == name)
            return item.value();
    }
    return QString();
}

/*!
  \class TypedefNode
 */

/*!
 */
TypedefNode::TypedefNode(Aggregate *parent, const QString& name)
    : LeafNode(Typedef, parent, name), associatedEnum_(0)
{
    setGenus(Node::CPP);
}

/*!
 */
void TypedefNode::setAssociatedEnum(const EnumNode *enume)
{
    associatedEnum_ = enume;
}

/*!
  \class Parameter
  \brief The class Parameter contains one parameter.

  A parameter can be a function parameter or a macro
  parameter.
 */

/*!
  Constructs this parameter from the left and right types
  \a dataType and rightType, the parameter \a name, and the
  \a defaultValue. In practice, \a rightType is not used,
  and I don't know what is was meant for.
 */
Parameter::Parameter(const QString& dataType,
                     const QString& rightType,
                     const QString& name,
                     const QString& defaultValue)
    : dataType_(dataType),
      rightType_(rightType),
      name_(name),
      defaultValue_(defaultValue)
{
    // nothing.
}

/*!
  Standard copy constructor copies \p.
 */
Parameter::Parameter(const Parameter& p)
    : dataType_(p.dataType_),
      rightType_(p.rightType_),
      name_(p.name_),
      defaultValue_(p.defaultValue_)
{
    // nothing.
}

/*!
  standard assignment operator assigns \p.
 */
Parameter& Parameter::operator=(const Parameter& p)
{
    dataType_ = p.dataType_;
    rightType_ = p.rightType_;
    name_ = p.name_;
    defaultValue_ = p.defaultValue_;
    return *this;
}

/*!
  Reconstructs the text describing the parameter and
  returns it. If \a value is true, the default value
  will be included, if there is one.
 */
QString Parameter::reconstruct(bool value) const
{
    QString p = dataType_ + rightType_;
    if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' ')))
        p += QLatin1Char(' ');
    p += name_;
    if (value && !defaultValue_.isEmpty())
        p += " = " + defaultValue_;
    return p;
}


/*!
  \class FunctionNode
 */

/*!
  Construct a function node for a C++ function. It's parent
  is \a parent, and it's name is \a name.

  Do not set overloadNumber_ in the initializer list because it
  is set by addChild() in the Node base class.
 */
FunctionNode::FunctionNode(Aggregate *parent, const QString& name)
    : LeafNode(Function, parent, name),
      metaness_(Plain),
      virtualness_(NonVirtual),
      const_(false),
      static_(false),
      reimplemented_(false),
      attached_(false),
      privateSignal_(false),
      overload_(false),
      reimplementedFrom_(0)
{
    setGenus(Node::CPP);
}

/*!
  Construct a function node for a QML method or signal, specified
  by \a type. It's parent is \a parent, and it's name is \a name.
  If \a attached is true, it is an attached method or signal.

  Do not set overloadNumber_ in the initializer list because it
  is set by addChild() in the Node base class.
 */
FunctionNode::FunctionNode(NodeType type, Aggregate *parent, const QString& name, bool attached)
    : LeafNode(type, parent, name),
      metaness_(Plain),
      virtualness_(NonVirtual),
      const_(false),
      static_(false),
      reimplemented_(false),
      attached_(attached),
      privateSignal_(false),
      overload_(false),
      reimplementedFrom_(0)
{
    setGenus(Node::QML);
    if (type == QmlMethod || type == QmlSignal) {
        if (name.startsWith("__"))
            setStatus(Internal);
    }
    else if (type == Function)
        setGenus(Node::CPP);
}

/*!
  Sets the \a virtualness of this function. If the \a virtualness
  is PureVirtual, and if the parent() is a ClassNode, set the parent's
  \e abstract flag to true.
 */
void FunctionNode::setVirtualness(Virtualness v)
{
    virtualness_ = v;
    if ((v == PureVirtual) && parent() && (parent()->type() == Node::Class))
        parent()->setAbstract(true);
}

/*! \fn void FunctionNode::setOverloadFlag(bool b)
  Sets this function node's overload flag to \a b.
  It does not set the overload number.
 */

/*! \fn void FunctionNode::setOverloadNumber(unsigned char n)
  Sets this function node's overload number to \a n.
  It does not set the overload flag.
 */

/*!
  Sets the function node's reimplementation flag to \a b.
  When \a b is true, it is supposed to mean that this function
  is a reimplementation of a virtual function in a base class,
  but it really just means the \e {\\reimp} command was seen in
  the qdoc comment.
 */
void FunctionNode::setReimplemented(bool b)
{
    reimplemented_ = b;
}

/*!
  Append \a parameter to the parameter list.
 */
void FunctionNode::addParameter(const Parameter& parameter)
{
    parameters_.append(parameter);
}

/*!
 */
void FunctionNode::borrowParameterNames(const FunctionNode *source)
{
    QVector<Parameter>::Iterator t = parameters_.begin();
    QVector<Parameter>::ConstIterator s = source->parameters_.constBegin();
    while (s != source->parameters_.constEnd() && t != parameters_.end()) {
        if (!(*s).name().isEmpty())
            (*t).setName((*s).name());
        ++s;
        ++t;
    }
}

/*!
  If this function is a reimplementation, \a from points
  to the FunctionNode of the function being reimplemented.
 */
void FunctionNode::setReimplementedFrom(FunctionNode *f)
{
    reimplementedFrom_ = f;
    f->reimplementedBy_.append(this);
}

/*!
  Adds the "associated" property \a p to this function node.
  The function might be the setter or getter for a property,
  for example.
 */
void FunctionNode::addAssociatedProperty(PropertyNode *p)
{
    associatedProperties_.append(p);
}

/*!
  Returns true if this function has at least one property
  that is active, i.e. at least one property that is not
  obsolete.
 */
bool FunctionNode::hasActiveAssociatedProperty() const
{
    if (associatedProperties_.isEmpty())
        return false;
    foreach (const PropertyNode* p, associatedProperties_) {
        if (!p->isObsolete())
            return true;
    }
    return false;
}

/*! \fn unsigned char FunctionNode::overloadNumber() const
  Returns the overload number for this function.
 */

/*!
  Returns the list of parameter names.
 */
QStringList FunctionNode::parameterNames() const
{
    QStringList names;
    QVector<Parameter>::ConstIterator p = parameters().constBegin();
    while (p != parameters().constEnd()) {
        names << (*p).name();
        ++p;
    }
    return names;
}

/*!
  Returns a raw list of parameters. If \a names is true, the
  names are included. If \a values is true, the default values
  are included, if any are present.
 */
QString FunctionNode::rawParameters(bool names, bool values) const
{
    QString raw;
    foreach (const Parameter &parameter, parameters()) {
        raw += parameter.dataType() + parameter.rightType();
        if (names)
            raw += parameter.name();
        if (values)
            raw += parameter.defaultValue();
    }
    return raw;
}

/*!
  Returns the list of reconstructed parameters. If \a values
  is true, the default values are included, if any are present.
 */
QStringList FunctionNode::reconstructParameters(bool values) const
{
    QStringList reconstructedParameters;
    QVector<Parameter>::ConstIterator p = parameters().constBegin();
    while (p != parameters().constEnd()) {
        reconstructedParameters << (*p).reconstruct(values);
        ++p;
    }
    return reconstructedParameters;
}

/*!
  Reconstructs and returns the function's signature. If \a values
  is true, the default values of the parameters are included, if
  present.
 */
QString FunctionNode::signature(bool values) const
{
    QString s;
    if (!returnType().isEmpty())
        s = returnType() + QLatin1Char(' ');
    s += name() + QLatin1Char('(');
    QStringList reconstructedParameters = reconstructParameters(values);
    int p = reconstructedParameters.size();
    if (p > 0) {
        for (int i=0; i<p; i++) {
            s += reconstructedParameters[i];
            if (i < (p-1))
                s += ", ";
        }
    }
    s += QLatin1Char(')');
    return s;
}

/*!
  Returns true if function \a fn has role \a r for this
  property.
 */
PropertyNode::FunctionRole PropertyNode::role(const FunctionNode* fn) const
{
    for (int i=0; i<4; i++) {
        if (functions_[i].contains(const_cast<FunctionNode*>(fn)))
            return (FunctionRole) i;
    }
    return Notifier;
}

/*!
  Print some debugging stuff.
 */
void FunctionNode::debug() const
{
    qDebug("QML METHOD %s returnType_ %s parentPath_ %s",
           qPrintable(name()), qPrintable(returnType_), qPrintable(parentPath_.join(' ')));
}

/*!
  \class PropertyNode

  This class describes one instance of using the Q_PROPERTY macro.
 */

/*!
  The constructor sets the \a parent and the \a name, but
  everything else is set to default values.
 */
PropertyNode::PropertyNode(Aggregate *parent, const QString& name)
    : LeafNode(Property, parent, name),
      stored_(FlagValueDefault),
      designable_(FlagValueDefault),
      scriptable_(FlagValueDefault),
      writable_(FlagValueDefault),
      user_(FlagValueDefault),
      const_(false),
      final_(false),
      revision_(-1),
      overrides_(0)
{
    setGenus(Node::CPP);
}

/*!
  Sets this property's \e {overridden from} property to
  \a baseProperty, which indicates that this property
  overrides \a baseProperty. To begin with, all the values
  in this property are set to the corresponding values in
  \a baseProperty.

  We probably should ensure that the constant and final
  attributes are not being overridden improperly.
 */
void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty)
{
    for (int i = 0; i < NumFunctionRoles; ++i) {
        if (functions_[i].isEmpty())
            functions_[i] = baseProperty->functions_[i];
    }
    if (stored_ == FlagValueDefault)
        stored_ = baseProperty->stored_;
    if (designable_ == FlagValueDefault)
        designable_ = baseProperty->designable_;
    if (scriptable_ == FlagValueDefault)
        scriptable_ = baseProperty->scriptable_;
    if (writable_ == FlagValueDefault)
        writable_ = baseProperty->writable_;
    if (user_ == FlagValueDefault)
        user_ = baseProperty->user_;
    overrides_ = baseProperty;
}

/*!
 */
QString PropertyNode::qualifiedDataType() const
{
    if (setters().isEmpty() && resetters().isEmpty()) {
        if (type_.contains(QLatin1Char('*')) || type_.contains(QLatin1Char('&'))) {
            // 'QWidget *' becomes 'QWidget *' const
            return type_ + " const";
        }
        else {
            /*
              'int' becomes 'const int' ('int const' is
              correct C++, but looks wrong)
            */
            return "const " + type_;
        }
    }
    else {
        return type_;
    }
}

bool QmlTypeNode::qmlOnly = false;
QMultiMap<QString,Node*> QmlTypeNode::inheritedBy;

/*!
  Constructs a Qml class node. The new node has the given
  \a parent and \a name.
 */
QmlTypeNode::QmlTypeNode(Aggregate *parent, const QString& name)
    : Aggregate(QmlType, parent, name),
      abstract_(false),
      cnodeRequired_(false),
      wrapper_(false),
      cnode_(0),
      logicalModule_(0),
      qmlBaseNode_(0)
{
    int i = 0;
    if (name.startsWith("QML:")) {
        qDebug() << "BOGUS QML qualifier:" << name;
        i = 4;
    }
    setTitle(name.mid(i));
    setPageType(Node::ApiPage);
    setGenus(Node::QML);
}

/*!
  Needed for printing a debug messages.
 */
QmlTypeNode::~QmlTypeNode()
{
    // nothing.
}

/*!
  Clear the static maps so that subsequent runs don't try to use
  contents from a previous run.
 */
void QmlTypeNode::terminate()
{
    inheritedBy.clear();
}

/*!
  Record the fact that QML class \a base is inherited by
  QML class \a sub.
 */
void QmlTypeNode::addInheritedBy(const QString& base, Node* sub)
{
    if (sub->isInternal())
        return;
    if (inheritedBy.constFind(base,sub) == inheritedBy.constEnd())
        inheritedBy.insert(base,sub);
}

/*!
  Loads the list \a subs with the nodes of all the subclasses of \a base.
 */
void QmlTypeNode::subclasses(const QString& base, NodeList& subs)
{
    subs.clear();
    if (inheritedBy.count(base) > 0) {
        subs = inheritedBy.values(base);
    }
}

QmlTypeNode* QmlTypeNode::qmlBaseNode()
{
    if (!qmlBaseNode_ && !qmlBaseName_.isEmpty()) {
        qmlBaseNode_ = QDocDatabase::qdocDB()->findQmlType(qmlBaseName_);
    }
    return qmlBaseNode_;
}

/*!
  If this QML type node has a base type node,
  return the fully qualified name of that QML
  type, i.e. <QML-module-name>::<QML-type-name>.
 */
QString QmlTypeNode::qmlFullBaseName() const
{
    QString result;
    if (qmlBaseNode_) {
        result = qmlBaseNode_->logicalModuleName() + "::" + qmlBaseNode_->name();
    }
    return result;
}

/*!
  If the QML type's QML module pointer is set, return the QML
  module name from the QML module node. Otherwise, return the
  empty string.
 */
QString QmlTypeNode::logicalModuleName() const
{
    return (logicalModule_ ? logicalModule_->logicalModuleName() : QString());
}

/*!
  If the QML type's QML module pointer is set, return the QML
  module version from the QML module node. Otherwise, return
  the empty string.
 */
QString QmlTypeNode::logicalModuleVersion() const
{
    return (logicalModule_ ? logicalModule_->logicalModuleVersion() : QString());
}

/*!
  If the QML type's QML module pointer is set, return the QML
  module identifier from the QML module node. Otherwise, return
  the empty string.
 */
QString QmlTypeNode::logicalModuleIdentifier() const
{
    return (logicalModule_ ? logicalModule_->logicalModuleIdentifier() : QString());
}

/*!
  Constructs a Qml basic type node. The new node has the given
  \a parent and \a name.
 */
QmlBasicTypeNode::QmlBasicTypeNode(Aggregate *parent,
                                   const QString& name)
    : Aggregate(QmlBasicType, parent, name)
{
    setTitle(name);
    setGenus(Node::QML);
}

/*!
  Constructor for the Qml property group node. \a parent is
  always a QmlTypeNode.
 */
QmlPropertyGroupNode::QmlPropertyGroupNode(QmlTypeNode* parent, const QString& name)
    : Aggregate(QmlPropertyGroup, parent, name)
{
    idNumber_ = -1;
    setGenus(Node::QML);
}

/*!
  Return the property group node's id number for use in
  constructing an id attribute for the property group.
  If the id number is currently -1, increment the global
  property group count and set the id number to the new
  value.
 */
QString QmlPropertyGroupNode::idNumber()
{
    if (idNumber_ == -1)
        idNumber_ = incPropertyGroupCount();
    return QString().setNum(idNumber_);
}

/*!
  Constructor for the QML property node.
 */
QmlPropertyNode::QmlPropertyNode(Aggregate* parent,
                                 const QString& name,
                                 const QString& type,
                                 bool attached)
    : LeafNode(QmlProperty, parent, name),
      type_(type),
      stored_(FlagValueDefault),
      designable_(FlagValueDefault),
      isAlias_(false),
      isdefault_(false),
      attached_(attached),
      readOnly_(FlagValueDefault)
{
    setPageType(ApiPage);
    if (type_ == QString("alias"))
        isAlias_ = true;
    if (name.startsWith("__"))
        setStatus(Internal);
    setGenus(Node::QML);
}

/*!
  Returns \c true if a QML property or attached property is
  not read-only. The algorithm for figuring this out is long
  amd tedious and almost certainly will break. It currently
  doesn't work for the qmlproperty:

  \code
      bool PropertyChanges::explicit,
  \endcode

  ...because the tokenizer gets confused on \e{explicit}.
 */
bool QmlPropertyNode::isWritable()
{
    if (readOnly_ != FlagValueDefault)
        return !fromFlagValue(readOnly_, false);

    QmlTypeNode* qcn = qmlTypeNode();
    if (qcn) {
        if (qcn->cppClassRequired()) {
            if (qcn->classNode()) {
                PropertyNode* pn = findCorrespondingCppProperty();
                if (pn)
                    return pn->isWritable();
                else
                    defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 "
                                             "in C++ class documented as QML type: "
                                             "(property not found in the C++ class or its base classes)")
                                          .arg(logicalModuleName()).arg(qmlTypeName()).arg(name()));
            }
            else
                defLocation().warning(tr("No Q_PROPERTY for QML property %1::%2::%3 "
                                         "in C++ class documented as QML type: "
                                         "(C++ class not specified or not found).")
                                      .arg(logicalModuleName()).arg(qmlTypeName()).arg(name()));
        }
    }
    return true;
}

/*!
  Returns a pointer this QML property's corresponding C++
  property, if it has one.
 */
PropertyNode* QmlPropertyNode::findCorrespondingCppProperty()
{
    PropertyNode* pn;
    Node* n = parent();
    while (n && !(n->isQmlType() || n->isJsType()))
        n = n->parent();
    if (n) {
        QmlTypeNode* qcn = static_cast<QmlTypeNode*>(n);
        ClassNode* cn = qcn->classNode();
        if (cn) {
            /*
              If there is a dot in the property name, first
              find the C++ property corresponding to the QML
              property group.
             */
            QStringList dotSplit = name().split(QChar('.'));
            pn = cn->findPropertyNode(dotSplit[0]);
            if (pn) {
                /*
                  Now find the C++ property corresponding to
                  the QML property in the QML property group,
                  <group>.<property>.
                */
                if (dotSplit.size() > 1) {
                    QStringList path(extractClassName(pn->qualifiedDataType()));
                    Node* nn = QDocDatabase::qdocDB()->findClassNode(path);
                    if (nn) {
                        ClassNode* cn = static_cast<ClassNode*>(nn);
                        PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
                        /*
                          If found, return the C++ property
                          corresponding to the QML property.
                          Otherwise, return the C++ property
                          corresponding to the QML property
                          group.
                        */
                        return (pn2 ? pn2 : pn);
                    }
                }
                else
                    return pn;
            }
        }
    }
    return 0;
}

/*!
  This returns the name of the owning QML type.
 */
QString QmlPropertyNode::element() const
{
    if (parent()->isQmlPropertyGroup())
        return parent()->element();
    return parent()->name();
}

/*!
  Construct the full document name for this node and return it.
 */
QString Node::fullDocumentName() const
{
    QStringList pieces;
    const Node* n = this;

    do {
        if (!n->name().isEmpty() && !n->isQmlPropertyGroup())
            pieces.insert(0, n->name());

        if ((n->isQmlType() || n->isJsType()) && !n->logicalModuleName().isEmpty()) {
            pieces.insert(0, n->logicalModuleName());
            break;
        }

        if (n->isDocumentNode())
            break;

        // Examine the parent node if one exists.
        if (n->parent())
            n = n->parent();
        else
            break;
    } while (true);

    // Create a name based on the type of the ancestor node.
    QString concatenator = "::";
    if (n->isQmlType() || n->isJsType())
        concatenator = QLatin1Char('.');

    if (n->isDocumentNode())
        concatenator = QLatin1Char('#');

    return pieces.join(concatenator);
}

/*!
  Returns the \a str as an NCName, which means the name can
  be used as the value of an \e id attribute. Search for NCName
  on the internet for details of what can be an NCName.
 */
QString Node::cleanId(const QString &str)
{
    QString clean;
    QString name = str.simplified();

    if (name.isEmpty())
        return clean;

    name = name.replace("::","-");
    name = name.replace(QLatin1Char(' '), QLatin1Char('-'));
    name = name.replace("()","-call");

    clean.reserve(name.size() + 20);
    if (!str.startsWith("id-"))
        clean = "id-";
    const QChar c = name[0];
    const uint u = c.unicode();

    if ((u >= 'a' && u <= 'z') ||
            (u >= 'A' && u <= 'Z') ||
            (u >= '0' && u <= '9')) {
        clean += c;
    }
    else if (u == '~') {
        clean += "dtor.";
    }
    else if (u == '_') {
        clean += "underscore.";
    }
    else {
        clean += QLatin1Char('a');
    }

    for (int i = 1; i < (int) name.length(); i++) {
        const QChar c = name[i];
        const uint u = c.unicode();
        if ((u >= 'a' && u <= 'z') ||
                (u >= 'A' && u <= 'Z') ||
                (u >= '0' && u <= '9') || u == '-' ||
                u == '_' || u == '.') {
            clean += c;
        }
        else if (c.isSpace() || u == ':' ) {
            clean += QLatin1Char('-');
        }
        else if (u == '!') {
            clean += "-not";
        }
        else if (u == '&') {
            clean += "-and";
        }
        else if (u == '<') {
            clean += "-lt";
        }
        else if (u == '=') {
            clean += "-eq";
        }
        else if (u == '>') {
            clean += "-gt";
        }
        else if (u == '#') {
            clean += "-hash";
        }
        else if (u == '(') {
            clean += QLatin1Char('-');
        }
        else if (u == ')') {
            clean += QLatin1Char('-');
        }
        else {
            clean += QLatin1Char('-');
            clean += QString::number((int)u, 16);
        }
    }
    return clean;
}

#if 0
/*!
  Creates a string that can be used as a UUID for the node,
  depending on the type and subtype of the node. Uniquenss
  is not guaranteed, but it is expected that strings created
  here will be unique within an XML document. Hence, the
  returned string can be used as the value of an \e id
  attribute.
 */
QString Node::idForNode() const
{
    const FunctionNode* func;
    const TypedefNode* tdn;
    QString str;

    switch (type()) {
    case Node::Namespace:
        str = "namespace-" + fullDocumentName();
        break;
    case Node::Class:
        str = "class-" + fullDocumentName();
        break;
    case Node::Enum:
        str = "enum-" + name();
        break;
    case Node::Typedef:
        tdn = static_cast<const TypedefNode*>(this);
        if (tdn->associatedEnum()) {
            return tdn->associatedEnum()->idForNode();
        }
        else {
            str = "typedef-" + name();
        }
        break;
    case Node::Function:
        func = static_cast<const FunctionNode*>(this);
        if (func->associatedProperty()) {
            return func->associatedProperty()->idForNode();
        }
        else {
            if (func->name().startsWith("operator")) {
                str.clear();
                /*
                  The test below should probably apply to all
                  functions, but for now, overloaded operators
                  are the only ones that produce duplicate id
                  attributes in the DITA XML files.
                 */
                if (relatesTo_)
                    str = "nonmember-";
                QString op = func->name().mid(8);
                if (!op.isEmpty()) {
                    int i = 0;
                    while (i<op.size() && op.at(i) == ' ')
                        ++i;
                    if (i>0 && i<op.size()) {
                        op = op.mid(i);
                    }
                    if (!op.isEmpty()) {
                        i = 0;
                        while (i < op.size()) {
                            const QChar c = op.at(i);
                            const uint u = c.unicode();
                            if ((u >= 'a' && u <= 'z') ||
                                    (u >= 'A' && u <= 'Z') ||
                                    (u >= '0' && u <= '9'))
                                break;
                            ++i;
                        }
                        str += "operator-";
                        if (i>0) {
                            QString tail = op.mid(i);
                            op = op.left(i);
                            if (operators_.contains(op)) {
                                str += operators_.value(op);
                                if (!tail.isEmpty())
                                    str += QLatin1Char('-') + tail;
                            }
                            else
                                qDebug() << "qdoc internal error: Operator missing from operators_ map:" << op;
                        }
                        else {
                            str += op;
                        }
                    }
                }
            }
            else if (parent_) {
                if (parent_->isClass())
                    str = "class-member-" + func->name();
                else if (parent_->isNamespace())
                    str = "namespace-member-" + func->name();
                else if (parent_->isQmlType())
                    str = "qml-method-" + parent_->name().toLower() + "-" + func->name();
                else if (parent_->isJsType())
                    str = "js-method-" + parent_->name().toLower() + "-" + func->name();
                else if (parent_->type() == Document) {
                    qDebug() << "qdoc internal error: Node subtype not handled:"
                             << parent_->docSubtype() << func->name();
                }
                else
                    qDebug() << "qdoc internal error: Node type not handled:"
                             << parent_->type() << func->name();

            }
            if (func->overloadNumber() != 0)
                str += QLatin1Char('-') + QString::number(func->overloadNumber());
        }
        break;
    case Node::QmlType:
        if (genus() == QML)
            str = "qml-class-" + name();
        else
            str = "js-type-" + name();
        break;
    case Node::QmlBasicType:
        if (genus() == QML)
            str = "qml-basic-type-" + name();
        else
            str = "js-basic-type-" + name();
        break;
    case Node::Document:
        {
            switch (docSubtype()) {
            case Node::Page:
            case Node::HeaderFile:
                str = title();
                if (str.isEmpty()) {
                    str = name();
                    if (str.endsWith(".html"))
                        str.remove(str.size()-5,5);
                }
                str.replace(QLatin1Char('/'), QLatin1Char('-'));
                break;
            case Node::File:
                str = name();
                str.replace(QLatin1Char('/'), QLatin1Char('-'));
                break;
            case Node::Example:
                str = name();
                str.replace(QLatin1Char('/'), QLatin1Char('-'));
                break;
            default:
                qDebug() << "ERROR: A case was not handled in Node::idForNode():"
                         << "docSubtype():" << docSubtype() << "type():" << type();
                break;
            }
        }
        break;
    case Node::Group:
    case Node::Module:
        str = title();
        if (str.isEmpty()) {
            str = name();
            if (str.endsWith(".html"))
                str.remove(str.size()-5,5);
        }
        str.replace(QLatin1Char('/'), QLatin1Char('-'));
        break;
    case Node::QmlModule:
        if (genus() == QML)
            str = "qml-module-" + name();
        else
            str = "js-module-" + name();
        break;
    case Node::QmlProperty:
        if (genus() == QML)
            str = "qml-";
        else
            str = "js-";
        if (isAttached())
            str += "attached-property-" + name();
        else
            str += "property-" + name();
        break;
    case Node::QmlPropertyGroup:
        {
            Node* n = const_cast<Node*>(this);
            if (genus() == QML)
                str = "qml-propertygroup-" + n->name();
            else
                str = "js-propertygroup-" + n->name();
        }
        break;
    case Node::Property:
        str = "property-" + name();
        break;
    case Node::QmlSignal:
        if (genus() == QML)
            str = "qml-signal-" + name();
        else
            str = "js-signal-" + name();
        break;
    case Node::QmlSignalHandler:
        if (genus() == QML)
            str = "qml-signal-handler-" + name();
        else
            str = "js-signal-handler-" + name();
        break;
    case Node::QmlMethod:
        func = static_cast<const FunctionNode*>(this);
        if (genus() == QML)
            str = "qml-method-";
        else
            str = "js-method-";
        str += parent_->name().toLower() + "-" + func->name();
        if (func->overloadNumber() != 0)
            str += QLatin1Char('-') + QString::number(func->overloadNumber());
        break;
    case Node::Variable:
        str = "var-" + name();
        break;
    default:
        qDebug() << "ERROR: A case was not handled in Node::idForNode():"
                 << "type():" << type() << "docSubtype():" << docSubtype();
        break;
    }
    if (str.isEmpty()) {
        qDebug() << "ERROR: A link text was empty in Node::idForNode():"
                 << "type():" << type() << "docSubtype():" << docSubtype()
                 << "name():" << name()
                 << "title():" << title();
    }
    else {
        str = cleanId(str);
    }
    return str;
}
#endif

/*!
  Prints the inner node's list of children.
  For debugging only.
 */
void Aggregate::printChildren(const QString& title)
{
    qDebug() << title << name() << children_.size();
    if (children_.size() > 0) {
        for (int i=0; i<children_.size(); ++i) {
            Node* n = children_.at(i);
            qDebug() << "  CHILD:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString();
        }
    }
}

/*!
  Returns \c true if the collection node's member list is
  not empty.
 */
bool CollectionNode::hasMembers() const
{
    return !members_.isEmpty();
}

/*!
  Appends \a node to the collection node's member list, if
  and only if it isn't already in the member list.
 */
void CollectionNode::addMember(Node* node)
{
    if (!members_.contains(node))
        members_.append(node);
}

/*!
  Returns \c true if this collection node contains at least
  one namespace node.
 */
bool CollectionNode::hasNamespaces() const
{
    if (!members_.isEmpty()) {
        NodeList::const_iterator i = members_.begin();
        while (i != members_.end()) {
            if ((*i)->isNamespace())
                return true;
            ++i;
        }
    }
    return false;
}

/*!
  Returns \c true if this collection node contains at least
  one class node.
 */
bool CollectionNode::hasClasses() const
{
    if (!members_.isEmpty()) {
        NodeList::const_iterator i = members_.cbegin();
        while (i != members_.cend()) {
            if ((*i)->isClass())
                return true;
            ++i;
        }
    }
    return false;
}

/*!
  Loads \a out with all this collection node's members that
  are namespace nodes.
 */
void CollectionNode::getMemberNamespaces(NodeMap& out)
{
    out.clear();
    NodeList::const_iterator i = members_.cbegin();
    while (i != members_.cend()) {
        if ((*i)->isNamespace())
            out.insert((*i)->name(),(*i));
        ++i;
    }
}

/*!
  Loads \a out with all this collection node's members that
  are class nodes.
 */
void CollectionNode::getMemberClasses(NodeMap& out) const
{
    out.clear();
    NodeList::const_iterator i = members_.cbegin();
    while (i != members_.cend()) {
        if ((*i)->isClass())
            out.insert((*i)->name(),(*i));
        ++i;
    }
}

/*!
  Prints the collection node's list of members.
  For debugging only.
 */
void CollectionNode::printMembers(const QString& title)
{
    qDebug() << title << name() << members_.size();
    if (members_.size() > 0) {
        for (int i=0; i<members_.size(); ++i) {
            Node* n = members_.at(i);
            qDebug() << "  MEMBER:" << n->name() << n->nodeTypeString() << n->nodeSubtypeString();
        }
    }
}

/*!
  Sets the document node's \a title. This is used for the page title.
 */
void CollectionNode::setTitle(const QString& title)
{
    title_ = title;
    parent()->addChild(this, title);
}

/*!
  This function splits \a arg on the blank character to get a
  logical module name and version number. If the version number
  is present, it spilts the version number on the '.' character
  to get a major version number and a minor vrsion number. If
  the version number is present, both the major and minor version
  numbers should be there, but the minor version number is not
  absolutely necessary.
 */
void CollectionNode::setLogicalModuleInfo(const QString& arg)
{
    QStringList blankSplit = arg.split(QLatin1Char(' '));
    logicalModuleName_ = blankSplit[0];
    if (blankSplit.size() > 1) {
        QStringList dotSplit = blankSplit[1].split(QLatin1Char('.'));
        logicalModuleVersionMajor_ = dotSplit[0];
        if (dotSplit.size() > 1)
            logicalModuleVersionMinor_ = dotSplit[1];
        else
            logicalModuleVersionMinor_ = "0";
    }
}

/*!
  This function accepts the logical module \a info as a string
  list. If the logical module info contains the version number,
  it spilts the version number on the '.' character to get the
  major and minor vrsion numbers. Both major and minor version
  numbers should be provided, but the minor version number is
  not strictly necessary.
 */
void CollectionNode::setLogicalModuleInfo(const QStringList& info)
{
    logicalModuleName_ = info[0];
    if (info.size() > 1) {
        QStringList dotSplit = info[1].split(QLatin1Char('.'));
        logicalModuleVersionMajor_ = dotSplit[0];
        if (dotSplit.size() > 1)
            logicalModuleVersionMinor_ = dotSplit[1];
        else
            logicalModuleVersionMinor_ = "0";
    }
}

QT_END_NAMESPACE
