/****************************************************************************
**
** 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$
**
****************************************************************************/

/*
  node.cpp
*/

#include "node.h"
#include "tree.h"
#include "codemarker.h"
#include <QUuid>
#include <qdebug.h>

QT_BEGIN_NAMESPACE

ExampleNodeMap ExampleNode::exampleNodeMap;

/*!
  \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 (par)
        par->removeChild(this);
    if (rel)
        rel->removeRelated(this);
}

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

/*!
  Construct a node with the given \a type and having the
  given \a parent and \a name. The new node is added to the
  parent's child list.
 */
Node::Node(Type type, InnerNode *parent, const QString& name)
    : typ(type),
      acc(Public),
      saf(UnspecifiedSafeness),
      pageTyp(NoPageType),
      sta(Commendable),
      par(parent),
      rel(0),
      nam(name)
{
    if (par)
        par->addChild(this);
}

/*!
  Returns the node's URL.
 */
QString Node::url() const
{
    return u;
}

/*!
  Sets the node's URL to \a url
 */
void Node::setUrl(const QString &url)
{
    u = url;
}

void Node::setPageType(const QString& t)
{
    if ((t == "API") || (t == "api"))
        pageTyp = ApiPage;
    else if (t == "article")
        pageTyp = ArticlePage;
    else if (t == "example")
        pageTyp = ExamplePage;
}

/*!
  Sets the pointer to the node that this node relates to.
 */
void Node::setRelates(InnerNode *pseudoParent)
{
    if (rel)
            rel->removeRelated(this);
    rel = pseudoParent;
    pseudoParent->related.append(this);
}

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

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

/*!
  Returns a string representing the access specifier.
 */
QString Node::accessString() const
{
    switch (acc) {
    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";
}

/*!
 */
Node::Status Node::inheritedStatus() const
{
    Status parentStatus = Commendable;
    if (par)
        parentStatus = par->inheritedStatus();
    return (Status)qMin((int)sta, (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 (par && saf == par->inheritedThreadSafeness())
        return UnspecifiedSafeness;
    return saf;
}

/*!
  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 (par && saf == UnspecifiedSafeness)
        return par->inheritedThreadSafeness();
    return saf;
}

/*!
  Returns the sanitized file name without the path.
  If the the file is an html file, the html suffix
  is removed. Why?
 */
QString Node::fileBase() const
{
    QString base = name();
    if (base.endsWith(".html"))
        base.chop(5);
    base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
    base = base.trimmed();
    base.replace(" ", "-");
    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()) {
        QUuid quuid = QUuid::createUuid();
        QString t = quuid.toString();
        uuid = "id-" + t.mid(1,t.length()-2);
    }
    return uuid;
}

/*!
  Composes a string to be used as an href attribute in DITA
  XML. It is composed of the file name and the UUID separated
  by a '#'. If this node is a class node, the file name is
  taken from this node; if this node is a function node, the
  file name is taken from the parent node of this node.
 */
QString Node::ditaXmlHref()
{
    QString href;
    if ((type() == Function) ||
        (type() == Property) ||
        (type() == Variable)) {
        href = parent()->fileBase();
    }
    else {
        href = fileBase();
    }
    if (!href.endsWith(".xml"))
        href += ".xml";
    return href + "#" + guid();
}

/*!
  If this node is a QML class node, return a pointer to it.
  If it is a child of a QML class node, return a pointer to
  the QML class node. Otherwise, return 0;
 */
const QmlClassNode* Node::qmlClassNode() const
{
    if (isQmlNode()) {
        const Node* n = this;
        while (n && n->subType() != Node::QmlClass)
            n = n->parent();
        if (n && n->subType() == Node::QmlClass)
            return static_cast<const QmlClassNode*>(n);
    }
    return 0;
}

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

/*!
  \class InnerNode
 */

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

/*!
  Find the node in this node's children that has the
  given \a name. If this node is a QML class node, be
  sure to also look in the children of its property
  group nodes. Return the matching node or 0.
 */
Node *InnerNode::findNode(const QString& name)
{
    Node *node = childMap.value(name);
    if (node && node->subType() != QmlPropertyGroup)
        return node;
    if ((type() == Fake) && (subType() == QmlClass)) {
        for (int i=0; i<children.size(); ++i) {
            Node* n = children.at(i);
            if (n->subType() == QmlPropertyGroup) {
                node = static_cast<InnerNode*>(n)->findNode(name);
                if (node)
                    return node;
            }
        }
    }
    return primaryFunctionMap.value(name);
}

/*!
  Same as the other findNode(), but if the node with the
  specified \a name is not of the specified \a type, return
  0.
 */
Node *InnerNode::findNode(const QString& name, Type type)
{
    if (type == Function) {
        return primaryFunctionMap.value(name);
    }
    else {
        Node *node = childMap.value(name);
        if (node && node->type() == type) {
            return node;
        }
        else {
            return 0;
        }
    }
}

/*!
  Find the function node in this node for the function named \a name.
 */
FunctionNode *InnerNode::findFunctionNode(const QString& name)
{
    return static_cast<FunctionNode *>(primaryFunctionMap.value(name));
}

/*!
  Find the function node in this node that has the same name as \a clone.
 */
FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone)
{
    QMap<QString, Node *>::ConstIterator c =
	    primaryFunctionMap.find(clone->name());
    if (c != primaryFunctionMap.end()) {
	if (isSameSignature(clone, (FunctionNode *) *c)) {
	    return (FunctionNode *) *c;
	}
        else if (secondaryFunctionMap.contains(clone->name())) {
	    const NodeList& secs = secondaryFunctionMap[clone->name()];
	    NodeList::ConstIterator s = secs.begin();
	    while (s != secs.end()) {
		if (isSameSignature(clone, (FunctionNode *) *s))
		    return (FunctionNode *) *s;
		++s;
	    }
	}
    }
    return 0;
}

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

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

/*!
 */
void InnerNode::setOverload(const FunctionNode *func, bool overlode)
{
    Node *node = (Node *) func;
    Node *&primary = primaryFunctionMap[func->name()];

    if (secondaryFunctionMap.contains(func->name())) {
	NodeList& secs = secondaryFunctionMap[func->name()];
	if (overlode) {
	    if (primary == node) {
		primary = secs.first();
		secs.erase(secs.begin());
		secs.append(node);
	    }
            else {
		secs.removeAll(node);
                secs.append(node);
            }
	}
        else {
	    if (primary != node) {
		secs.removeAll(node);
		secs.prepend(primary);
		primary = node;
	    }
	}
    }
}

/*!
  Mark all child nodes that have no documentation as having
  private access and internal status. qdoc will then ignore
  them for documentation purposes.
 */
void InnerNode::makeUndocumentedChildrenInternal()
{
    foreach (Node *child, childNodes()) {
        if (child->doc().isEmpty()) {
            child->setAccess(Node::Private);
            child->setStatus(Node::Internal);
        }
    }
}

/*!
 */
void InnerNode::normalizeOverloads()
{
    QMap<QString, Node *>::Iterator p1 = primaryFunctionMap.begin();
    while (p1 != primaryFunctionMap.end()) {
        FunctionNode *primaryFunc = (FunctionNode *) *p1;
        if (secondaryFunctionMap.contains(primaryFunc->name()) &&
            (primaryFunc->status() != Commendable ||
             primaryFunc->access() == Private)) {

	    NodeList& secs = secondaryFunctionMap[primaryFunc->name()];
	    NodeList::ConstIterator s = secs.begin();
	    while (s != secs.end()) {
		FunctionNode *secondaryFunc = (FunctionNode *) *s;

                // Any non-obsolete, non-compatibility, non-private functions
                // (i.e, visible functions) are preferable to the primary
                // function.

                if (secondaryFunc->status() == Commendable &&
                    secondaryFunc->access() != Private) {

                    *p1 = secondaryFunc;
                    int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc);
                    secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc);
                    break;
                }
                ++s;
            }
        }
        ++p1;
    }

    QMap<QString, Node *>::ConstIterator p = primaryFunctionMap.begin();
    while (p != primaryFunctionMap.end()) {
	FunctionNode *primaryFunc = (FunctionNode *) *p;
	if (primaryFunc->isOverload())
	    primaryFunc->ove = false;
	if (secondaryFunctionMap.contains(primaryFunc->name())) {
	    NodeList& secs = secondaryFunctionMap[primaryFunc->name()];
	    NodeList::ConstIterator s = secs.begin();
	    while (s != secs.end()) {
		FunctionNode *secondaryFunc = (FunctionNode *) *s;
		if (!secondaryFunc->isOverload())
		    secondaryFunc->ove = true;
		++s;
	    }
	}
	++p;
    }

    NodeList::ConstIterator c = childNodes().begin();
    while (c != childNodes().end()) {
	if ((*c)->isInnerNode())
	    ((InnerNode *) *c)->normalizeOverloads();
	++c;
    }
}

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

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

/*!
 */
void InnerNode::deleteChildren()
{
    NodeList childrenCopy = children; // `children` will be changed in ~Node()
    qDeleteAll(childrenCopy);
}

/*!
  Returns true because this is an inner node.
 */
bool InnerNode::isInnerNode() const
{
    return true;
}

/*!
 */
const Node *InnerNode::findNode(const QString& name) const
{
    InnerNode *that = (InnerNode *) this;
    return that->findNode(name);
}

/*!
 */
const Node *InnerNode::findNode(const QString& name, Type type) const
{
    InnerNode *that = (InnerNode *) this;
    return that->findNode(name, type);
}

/*!
  Find the function node in this node that has the given \a name.
 */
const FunctionNode *InnerNode::findFunctionNode(const QString& name) const
{
    InnerNode *that = (InnerNode *) this;
    return that->findFunctionNode(name);
}

/*!
  Find the function node in this node that has the same name as \a clone.
 */
const FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) const
{
    InnerNode *that = (InnerNode *) this;
    return that->findFunctionNode(clone);
}

/*!
 */
const EnumNode *InnerNode::findEnumNodeForValue(const QString &enumValue) const
{
    foreach (const Node *node, enumChildren) {
        const EnumNode *enume = static_cast<const EnumNode *>(node);
        if (enume->hasItem(enumValue))
            return enume;
    }
    return 0;
}

/*!
  Returnds the sequence number of the function node \a func
  in the list of overloaded functions for a class, such that
  all the functions have the same name as the \a func.
 */
int InnerNode::overloadNumber(const FunctionNode *func) const
{
    Node *node = (Node *) func;
    if (primaryFunctionMap[func->name()] == node) {
        return 1;
    }
    else {
        return secondaryFunctionMap[func->name()].indexOf(node) + 2;
    }
}

/*!
  Returns the number of member functions of a class such that
  the functions are all named \a funcName.
 */
int InnerNode::numOverloads(const QString& funcName) const
{
    if (primaryFunctionMap.contains(funcName)) {
        return secondaryFunctionMap[funcName].count() + 1;
    }
    else {
        return 0;
    }
}

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

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

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

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

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

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

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

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

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

/*!
  Adds the \a child to this node's child list.
 */
void InnerNode::addChild(Node *child)
{
    children.append(child);
    if ((child->type() == Function) || (child->type() == QmlMethod)) {
        FunctionNode *func = (FunctionNode *) child;
        if (!primaryFunctionMap.contains(func->name())) {
            primaryFunctionMap.insert(func->name(), func);
	}
        else {
	    NodeList &secs = secondaryFunctionMap[func->name()];
	    secs.append(func);
	}
    }
    else {
        if (child->type() == Enum)
            enumChildren.append(child);
        childMap.insert(child->name(), child);
    }
}

/*!
 */
void InnerNode::removeChild(Node *child)
{
    children.removeAll(child);
    enumChildren.removeAll(child);
    if (child->type() == Function) {
	QMap<QString, Node *>::Iterator prim =
		primaryFunctionMap.find(child->name());
	NodeList& secs = secondaryFunctionMap[child->name()];
	if (prim != primaryFunctionMap.end() && *prim == child) {
	    if (secs.isEmpty()) {
		primaryFunctionMap.remove(child->name());
	    }
            else {
		primaryFunctionMap.insert(child->name(), secs.takeFirst());
	    }
	}
        else {
	    secs.removeAll(child);
	}
        QMap<QString, Node *>::Iterator ent = childMap.find( child->name() );
        if (ent != childMap.end() && *ent == child)
            childMap.erase( ent );
    }
    else {
	QMap<QString, Node *>::Iterator ent = childMap.find(child->name());
	if (ent != childMap.end() && *ent == child)
	    childMap.erase(ent);
    }
}

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

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

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

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

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

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

    QString moduleName = moduleDir.left(finish);

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

/*!
 */
void InnerNode::removeRelated(Node *pseudoChild)
{
    related.removeAll(pseudoChild);
}

/*!
  \class LeafNode
 */

/*!
  Returns false because this is a LeafNode.
 */
bool LeafNode::isInnerNode() const
{
    return false;
}

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

/*!
  \class NamespaceNode
 */

/*!
  Constructs a namespace node.
 */
NamespaceNode::NamespaceNode(InnerNode *parent, const QString& name)
    : InnerNode(Namespace, parent, name)
{
    setPageType(ApiPage);
}

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

/*!
  Constructs a class node. A class node will generate an API page.
 */
ClassNode::ClassNode(InnerNode *parent, const QString& name)
    : InnerNode(Class, parent, name)
{
    hidden = false;
    abstract = false;
    setPageType(ApiPage);
}

/*!
 */
void ClassNode::addBaseClass(Access access,
                             ClassNode *node,
                             const QString &dataTypeWithTemplateArgs)
{
    bases.append(RelatedClass(access, node, dataTypeWithTemplateArgs));
    node->derived.append(RelatedClass(access, this));
}

/*!
 */
void ClassNode::fixBaseClasses()
{
    int i;
    i = 0;
    while (i < bases.size()) {
        ClassNode* bc = bases.at(i).node;
        if (bc->access() == Node::Private) {
            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;
        }
    }

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

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

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

    const PropertyNode *pn = 0;

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

    return pn;
}

/*!
  \class FakeNode
 */

/*!
  The type of a FakeNode is Fake, and it has a \a subtype,
  which specifies the type of FakeNode. The page type for
  the page index is set here.
 */
FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subtype)
    : InnerNode(Fake, parent, name), sub(subtype)
{
    switch (subtype) {
    case Module:
    case Page:
    case Group:
        setPageType(ArticlePage);
        break;
    case QmlClass:
    case QmlBasicType:
        setPageType(ApiPage);
        break;
    case Example:
        setPageType(ExamplePage);
        break;
    default:
        break;
    }
}

/*!
  Returns the fake node's title. This is used for the page title.
*/
QString FakeNode::title() const
{
    return tle;
}

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

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

    if ((sub == File) || (sub == Image)) {
        if (title().isEmpty() && name().contains("/"))
            return name();
    }
    return QString();
}

/*!
  The constructor calls the FakeNode constructor with
  \a parent, \a name, and Node::Example.
 */
ExampleNode::ExampleNode(InnerNode* parent, const QString& name)
    : FakeNode(parent, name, Node::Example)
{
    // nothing
}

/*!
  \class EnumNode
 */

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

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

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

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

/*!
  \class TypedefNode
 */

/*!
 */
TypedefNode::TypedefNode(InnerNode *parent, const QString& name)
    : LeafNode(Typedef, parent, name), ae(0)
{
}

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

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

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

/*!
  Constructs this parameter from the left and right types
  \a leftType and rightType, the parameter \a name, and the
  \a defaultValue. In practice, \a rightType is not used,
  and I don't know what is was meant for.
 */
Parameter::Parameter(const QString& leftType,
                     const QString& rightType,
		     const QString& name,
                     const QString& defaultValue)
    : lef(leftType), rig(rightType), nam(name), def(defaultValue)
{
}

/*!
  The standard copy constructor copies the strings from \a p.
 */
Parameter::Parameter(const Parameter& p)
    : lef(p.lef), rig(p.rig), nam(p.nam), def(p.def)
{
}

/*!
  Assigning Parameter \a p to this Parameter copies the
  strings across.
 */
Parameter& Parameter::operator=(const Parameter& p)
{
    lef = p.lef;
    rig = p.rig;
    nam = p.nam;
    def = p.def;
    return *this;
}

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


/*!
  \class FunctionNode
 */

/*!
  Construct a function node for a C++ function. It's parent
  is \a parent, and it's name is \a name.
 */
FunctionNode::FunctionNode(InnerNode *parent, const QString& name)
    : LeafNode(Function, parent, name),
      met(Plain),
      vir(NonVirtual),
      con(false),
      sta(false),
      ove(false),
      att(false),
      rf(0),
      ap(0)
{
    // nothing.
}

/*!
  Construct a function node for a QML method or signal, specified
  by \a type. It's parent is \a parent, and it's name is \a name.
  If \a attached is true, it is an attached method or signal.
 */
FunctionNode::FunctionNode(Type type, InnerNode *parent, const QString& name, bool attached)
    : LeafNode(type, parent, name),
      met(Plain),
      vir(NonVirtual),
      con(false),
      sta(false),
      ove(false),
      att(attached),
      rf(0),
      ap(0)
{
    // nothing.
}

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

/*!
 */
void FunctionNode::setOverload(bool overlode)
{
    parent()->setOverload(this, overlode);
    ove = overlode;
}

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

/*!
 */
void FunctionNode::addParameter(const Parameter& parameter)
{
    params.append(parameter);
}

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

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

/*!
  Sets the "associated" property to \a property. The function
  might be the setter or getter for a property, for example.
 */
void FunctionNode::setAssociatedProperty(PropertyNode *property)
{
    ap = property;
}

/*!
  Returns the overload number for this function obtained
  from the parent.
 */
int FunctionNode::overloadNumber() const
{
    return parent()->overloadNumber(this);
}

/*!
  Returns the number of times this function name has been
  overloaded, obtained from the parent.
 */
int FunctionNode::numOverloads() const
{
    return parent()->numOverloads(name());
}

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

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

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

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

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

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

/*!
  \class PropertyNode

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

/*!
  The constructor sets the \a parent and the \a name, but
  everything else is set to default values.
 */
PropertyNode::PropertyNode(InnerNode *parent, const QString& name)
    : LeafNode(Property, parent, name),
      sto(Trool_Default),
      des(Trool_Default),
      scr(Trool_Default),
      wri(Trool_Default),
      usr(Trool_Default),
      cst(false),
      fnl(false),
      rev(-1),
      overrides(0)
{
    // nothing.
}

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

  We probably should ensure that the constant and final
  attributes are not being overridden improperly.
 */
void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty)
{
    for (int i = 0; i < NumFunctionRoles; ++i) {
        if (funcs[i].isEmpty())
            funcs[i] = baseProperty->funcs[i];
    }
    if (sto == Trool_Default)
        sto = baseProperty->sto;
    if (des == Trool_Default)
        des = baseProperty->des;
    if (scr == Trool_Default)
        scr = baseProperty->scr;
    if (wri == Trool_Default)
        wri = baseProperty->wri;
    if (usr == Trool_Default)
        usr = baseProperty->usr;
    overrides = baseProperty;
}

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

/*! Converts the \a boolean value 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.
 */
PropertyNode::Trool PropertyNode::toTrool(bool boolean)
{
    return boolean ? Trool_True : Trool_False;
}

/*!
  Converts the enum \a troolean back to a boolean value.
  If \a troolean 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 PropertyNode::fromTrool(Trool troolean, bool defaultValue)
{
    switch (troolean) {
    case Trool_True:
        return true;
    case Trool_False:
        return false;
    default:
        return defaultValue;
    }
}

/*!
  \class TargetNode
 */

/*!
 */
TargetNode::TargetNode(InnerNode *parent, const QString& name)
    : LeafNode(Target, parent, name)
{
}

/*!
  Returns false because this is a TargetNode.
 */
bool TargetNode::isInnerNode() const
{
    return false;
}

#ifdef QDOC_QML
bool QmlClassNode::qmlOnly = false;
QMultiMap<QString,Node*> QmlClassNode::inheritedBy;

/*!
  Constructs a Qml class node (i.e. a Fake node with the
  subtype QmlClass. The new node has the given \a parent
  and \a name and is associated with the C++ class node
  specified by \a cn which may be null if the the Qml
  class node is not associated with a C++ class node.
 */
QmlClassNode::QmlClassNode(InnerNode *parent,
                           const QString& name,
                           const ClassNode* cn)
    : FakeNode(parent, name, QmlClass), cnode(cn)
{
    if (name.startsWith(QLatin1String("QML:")))
        setTitle((qmlOnly ? QLatin1String("") : QLatin1String("QML ")) + name.mid(4) + QLatin1String(" Element"));
    else
        setTitle((qmlOnly ? QLatin1String("") : QLatin1String("QML ")) + name + QLatin1String(" Element"));
}

/*!
  I made this so I could print a debug message here.
 */
QmlClassNode::~QmlClassNode()
{
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
    qDebug() << "Deleting QmlClassNode:" << name();
#endif
}

/*!
  Clear the multimap so that subsequent runs don't try to use
  nodes from a previous run.
 */
void QmlClassNode::clear()
{
    inheritedBy.clear();
}

/*!
  The base file name for this kind of node has "qml_"
  prepended to it.

  But not yet. Still testing.
 */
QString QmlClassNode::fileBase() const
{
    return Node::fileBase();
}

/*!
  Record the fact that QML class \a base is inherited by
  QML class \a sub.
 */
void QmlClassNode::addInheritedBy(const QString& base, Node* sub)
{
    inheritedBy.insert(base,sub);
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
    qDebug() << "QmlClassNode::addInheritedBy(): insert" << base << sub->name() << inheritedBy.size();
#endif
}

/*!
  Loads the list \a subs with the nodes of all the subclasses of \a base.
 */
void QmlClassNode::subclasses(const QString& base, NodeList& subs)
{
    subs.clear();
    if (inheritedBy.count(base) > 0) {
        subs = inheritedBy.values(base);
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES
        qDebug() << "QmlClassNode::subclasses():" <<  inheritedBy.count(base) << base
                 << "subs:" << subs.size() << "total size:" << inheritedBy.size();
#endif
    }
}

/*!
  Constructs a Qml basic type node (i.e. a Fake node with
  the subtype QmlBasicType. The new node has the given
  \a parent and \a name.
 */
QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent,
                                   const QString& name)
    : FakeNode(parent, name, QmlBasicType)
{
    setTitle(name);
}

/*!
  Constructor for the Qml property group node. \a parent is
  always a QmlClassNode.
 */
QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent,
                                   const QString& name,
                                   bool attached)
    : FakeNode(parent, name, QmlPropertyGroup),
      isdefault(false),
      att(attached)
{
    // nothing.
}

/*!
  Constructor for the QML property node.
 */
QmlPropertyNode::QmlPropertyNode(QmlPropGroupNode *parent,
                                 const QString& name,
                                 const QString& type,
                                 bool attached)
    : LeafNode(QmlProperty, parent, name),
      dt(type),
      sto(Trool_Default),
      des(Trool_Default),
      att(attached)
{
    setPageType(ApiPage);
}

/*!
  I don't know what this is.
 */
QmlPropertyNode::Trool QmlPropertyNode::toTrool(bool boolean)
{
    return boolean ? Trool_True : Trool_False;
}

/*!
  I don't know what this is either.
 */
bool QmlPropertyNode::fromTrool(Trool troolean, bool defaultValue)
{
    switch (troolean) {
    case Trool_True:
        return true;
    case Trool_False:
        return false;
    default:
        return defaultValue;
    }
}

/*!
  Returns true if a QML property or attached property is
  read-only. The algorithm for figuring this out is long
  amd tedious and almost certainly will break. It currently
  doesn't work for qmlproperty bool PropertyChanges::explicit,
  because the tokenizer gets confused on "explicit".
 */
bool QmlPropertyNode::isWritable(const Tree* tree) const
{
    if (wri != Trool_Default)
        return fromTrool(wri, false);

    const PropertyNode *pn = correspondingProperty(tree);
    if (pn)
        return pn->isWritable();
    else {
        location().warning(tr("Can't determine read-only status of QML property %1; writable assumed.").arg(name()));
        return true;
    }
}

const PropertyNode *QmlPropertyNode::correspondingProperty(const Tree *tree) const
{
    const PropertyNode *pn;

    Node* n = parent();
    while (n && n->subType() != Node::QmlClass)
        n = n->parent();
    if (n) {
        const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n);
        const ClassNode* cn = qcn->classNode();
        if (cn) {
            QStringList dotSplit = name().split(QChar('.'));
            pn = cn->findPropertyNode(dotSplit[0]);
            if (pn) {
                if (dotSplit.size() > 1) {
                    // Find the C++ property corresponding to the QML property in
                    // the property group, <group>.<property>.

                    QStringList path(extractClassName(pn->qualifiedDataType()));
                    const Node* nn = tree->findNode(path,Class);
                    if (nn) {
                        const ClassNode* cn = static_cast<const ClassNode*>(nn);
                        const PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
                        if (pn2)
                            return pn2; // Return the property for the QML property.
                        else
                            return pn;  // Return the property for the QML group.
                    }
                }
                else
                    return pn;
            }
            else {
                pn = cn->findPropertyNode(dotSplit[0]);
                if (pn)
                    return pn;
            }
        }
    }

    return 0;
}

#endif

QT_END_NAMESPACE
