From: Lars Knoll
Date: Mon, 5 Mar 2012 14:34:40 +0000 (+0100)
Subject: Move qdoc into qtbase and bootstrap it
X-Git-Tag: qt-v5.0.0-alpha1~489
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=448a3cfe17735499cb43186200e8d52669b6a7ca;p=profile%2Fivi%2Fqtbase.git
Move qdoc into qtbase and bootstrap it
We need qdoc in qtbase to be able to properly
modularize our documentation and build it
when building the different Qt modules.
qdoc does contain a copy of the qml parser from
qmldevtools, but this is the lesser evil compared
to how we are currently forced to genereate our
docs (and the fact that no developer can run
qdoc and check the docs for their module).
Change-Id: I9f748459382a11cf5d5153d1ee611d7a5d3f4ac1
Reviewed-by: Casper van Donderen
Reviewed-by: Martin Smith
---
diff --git a/configure b/configure
index f29df99..151ddb0 100755
--- a/configure
+++ b/configure
@@ -7169,7 +7169,7 @@ for file in .projects .projects.3; do
fi
SPEC=$XQMAKESPEC ;;
*/qmake/qmake.pro) continue ;;
- *tools/bootstrap*|*tools/moc*|*tools/rcc*|*tools/uic*) SPEC=$QMAKESPEC ;;
+ *tools/bootstrap*|*tools/moc*|*tools/rcc*|*tools/uic*|*tools/qdoc*) SPEC=$QMAKESPEC ;;
*) if [ "$CFG_NOPROCESS" = "yes" ]; then
continue
else
diff --git a/src/tools/qdoc/TODO.txt b/src/tools/qdoc/TODO.txt
new file mode 100644
index 0000000..9bf4a28
--- /dev/null
+++ b/src/tools/qdoc/TODO.txt
@@ -0,0 +1,87 @@
+ * fix QWSPointerCalibrationData::devPoints and qwsServer
+ * Fix QMenu::addAction(QAction *) overload, "using" etc.
+ * fix space between two tables using
+ * qpixmap-qt3.html; remove 8 public functions inherited from QPaintDevice
+ * \variable array
+ * Added support for parameterless macros (e.g. \macro Q_OBJECT).
+ * Made qdoc stricter regarding the data types (e.g. can't use \enum to document a typedef).
+ * Parse QT_MODULE() macro and generate proper warnings for the various editions.
+ * Fix parsing of \image following \value (e.g. qt.html).
+ * Don't turn X11 and similar names into links.
+ * Added automatic links from getters to setters and vice versa.
+ * Support \module and show which module each class is from.
+ * Fix occasional crash caused by misuse of const_cast().
+ * Provide clearer error messages when resolves fail.
+
+
+
+
+CHECK:
+
+ * Identify editions
+ * Automatic \sa getter setter
+ * \macro Q_OBJECT
+
+MUST HAVES:
+
+ * resolve [gs]etters for \sa using base class
+
+ * fix \overload when one is a signal and the other a normal function
+ * use "project" for .dcf files
+ * functions.html: include the types from QtGlobal as well as the functions (whatever that means)
+
+ * nice template function/class syntax
+ * spellchecker: built-in vs. builtin
+
+ * verbose mode for functions that don't exist
+ * No links to Porting Guide sections (e.g. QStringList)
+ * link toggled(bool)
+ * autolink foo(1)
+ * handle using correctly
+ * QObject "reentrant" list: duplicates
+ * operator<< \overload
+ * \compat \overload
+ * qWarning() link
+ * operator<<() autolink
+
+ * get rid of spurious 'global' functions
+ * Make automatic links in code work
+
+ * Fix encoding bug (see Important email from Simon Hausmann)
+ * Make links to QFoo::bar().baz() work
+ * Fix automatic links in \sectionX (e.g. qt4-getting-started.html)
+ * Provide a "List of all properties" page.
+
+ * expand QObjectList -> QList
+ * warning for unnamed parameters in property access functions
+ * \center...\endcenter
+
+LINKS:
+
+ * explanation following nonstandard wording warning
+
+ * omit \overload in operator<< and operator>>
+ * make operator-() unary and binary independent functions (no \overload)
+ * fix \overload
+ * fix \legalese
+ * remove warning for undocumented enum item like QLocale::LastLanguage
+ * improve the \a warnings for overloads; if one overload documents a para, fine
+
+ * implement \sidebar
+
+ * implement \legalesefile
+
+ * show in which module each class is
+ * list namespaces, list header files
+
+
+NICE FEATURES:
+ * implement inheritance tree for each class (as a PNG)
+ * avoid ...
in table/item cells without relying on horrible kludge
+ * prevent macros from having same name as commands
+ * be smart about enum types Foo::Bar vs. Bar when comparing functions
+ * be smart about const & non-const when comparing functions
+
+OTHER:
+ * make qdoc run faster
+ * make sure \headerfile works even if specified after \relates
diff --git a/src/tools/qdoc/atom.cpp b/src/tools/qdoc/atom.cpp
new file mode 100644
index 0000000..777240c
--- /dev/null
+++ b/src/tools/qdoc/atom.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include
+#include "atom.h"
+#include "location.h"
+#include
+
+QT_BEGIN_NAMESPACE
+
+QLatin1String Atom::BOLD_ ("bold");
+QLatin1String Atom::INDEX_ ("index");
+QLatin1String Atom::ITALIC_ ("italic");
+QLatin1String Atom::LINK_ ("link");
+QLatin1String Atom::PARAMETER_ ("parameter");
+QLatin1String Atom::SPAN_ ("span");
+QLatin1String Atom::SUBSCRIPT_ ("subscript");
+QLatin1String Atom::SUPERSCRIPT_ ("superscript");
+QLatin1String Atom::TELETYPE_ ("teletype");
+QLatin1String Atom::UNDERLINE_ ("underline");
+
+QLatin1String Atom::BULLET_ ("bullet");
+QLatin1String Atom::TAG_ ("tag");
+QLatin1String Atom::VALUE_ ("value");
+QLatin1String Atom::LOWERALPHA_ ("loweralpha");
+QLatin1String Atom::LOWERROMAN_ ("lowerroman");
+QLatin1String Atom::NUMERIC_ ("numeric");
+QLatin1String Atom::UPPERALPHA_ ("upperalpha");
+QLatin1String Atom::UPPERROMAN_ ("upperroman");
+
+/*! \class Atom
+ \brief The Atom class is the fundamental unit for representing
+ documents internally.
+
+ Atoms have a \i type and are completed by a \i string whose
+ meaning depends on the \i type. For example, the string
+ \quotation
+ \i italic text looks nicer than \bold bold text
+ \endquotation
+ is represented by the following atoms:
+ \quotation
+ (FormattingLeft, ATOM_FORMATTING_ITALIC)
+ (String, "italic")
+ (FormattingRight, ATOM_FORMATTING_ITALIC)
+ (String, " text is more attractive than ")
+ (FormattingLeft, ATOM_FORMATTING_BOLD)
+ (String, "bold")
+ (FormattingRight, ATOM_FORMATTING_BOLD)
+ (String, " text")
+ \endquotation
+
+ \also Text
+*/
+
+/*! \enum Atom::Type
+
+ \value AbstractLeft
+ \value AbstractRight
+ \value AnnotatedList
+ \value AutoLink
+ \value BaseName
+ \value BriefLeft
+ \value BriefRight
+ \value C
+ \value CaptionLeft
+ \value CaptionRight
+ \value Code
+ \value CodeBad
+ \value CodeNew
+ \value CodeOld
+ \value CodeQuoteArgument
+ \value CodeQuoteCommand
+ \value DivLeft
+ \value DivRight
+ \value EndQmlText
+ \value FormatElse
+ \value FormatEndif
+ \value FormatIf
+ \value FootnoteLeft
+ \value FootnoteRight
+ \value FormattingLeft
+ \value FormattingRight
+ \value GeneratedList
+ \value Image
+ \value ImageText
+ \value ImportantNote
+ \value InlineImage
+ \value LineBreak
+ \value Link
+ \value LinkNode
+ \value ListLeft
+ \value ListItemNumber
+ \value ListTagLeft
+ \value ListTagRight
+ \value ListItemLeft
+ \value ListItemRight
+ \value ListRight
+ \value Nop
+ \value Note
+ \value ParaLeft
+ \value ParaRight
+ \value Qml
+ \value QmlText
+ \value QuotationLeft
+ \value QuotationRight
+ \value RawString
+ \value SectionLeft
+ \value SectionRight
+ \value SectionHeadingLeft
+ \value SectionHeadingRight
+ \value SidebarLeft
+ \value SidebarRight
+ \value SinceList
+ \value String
+ \value TableLeft
+ \value TableRight
+ \value TableHeaderLeft
+ \value TableHeaderRight
+ \value TableRowLeft
+ \value TableRowRight
+ \value TableItemLeft
+ \value TableItemRight
+ \value TableOfContents
+ \value Target
+ \value UnhandledFormat
+ \value UnknownCommand
+*/
+
+static const struct {
+ const char *english;
+ int no;
+} atms[] = {
+ { "AbstractLeft", Atom::AbstractLeft },
+ { "AbstractRight", Atom::AbstractRight },
+ { "AnnotatedList", Atom::AnnotatedList },
+ { "AutoLink", Atom::AutoLink },
+ { "BaseName", Atom::BaseName },
+ { "BriefLeft", Atom::BriefLeft },
+ { "BriefRight", Atom::BriefRight },
+ { "C", Atom::C },
+ { "CaptionLeft", Atom::CaptionLeft },
+ { "CaptionRight", Atom::CaptionRight },
+ { "Code", Atom::Code },
+ { "CodeBad", Atom::CodeBad },
+ { "CodeNew", Atom::CodeNew },
+ { "CodeOld", Atom::CodeOld },
+ { "CodeQuoteArgument", Atom::CodeQuoteArgument },
+ { "CodeQuoteCommand", Atom::CodeQuoteCommand },
+ { "DivLeft", Atom::DivLeft },
+ { "DivRight", Atom::DivRight },
+ { "EndQmlText", Atom::EndQmlText },
+ { "FootnoteLeft", Atom::FootnoteLeft },
+ { "FootnoteRight", Atom::FootnoteRight },
+ { "FormatElse", Atom::FormatElse },
+ { "FormatEndif", Atom::FormatEndif },
+ { "FormatIf", Atom::FormatIf },
+ { "FormattingLeft", Atom::FormattingLeft },
+ { "FormattingRight", Atom::FormattingRight },
+ { "GeneratedList", Atom::GeneratedList },
+ { "GuidLink", Atom::GuidLink},
+ { "Image", Atom::Image },
+ { "ImageText", Atom::ImageText },
+ { "ImportantLeft", Atom::ImportantLeft },
+ { "ImportantRight", Atom::ImportantRight },
+ { "InlineImage", Atom::InlineImage },
+ { "JavaScript", Atom::JavaScript },
+ { "EndJavaScript", Atom::EndJavaScript },
+ { "LegaleseLeft", Atom::LegaleseLeft },
+ { "LegaleseRight", Atom::LegaleseRight },
+ { "LineBreak", Atom::LineBreak },
+ { "Link", Atom::Link },
+ { "LinkNode", Atom::LinkNode },
+ { "ListLeft", Atom::ListLeft },
+ { "ListItemNumber", Atom::ListItemNumber },
+ { "ListTagLeft", Atom::ListTagLeft },
+ { "ListTagRight", Atom::ListTagRight },
+ { "ListItemLeft", Atom::ListItemLeft },
+ { "ListItemRight", Atom::ListItemRight },
+ { "ListRight", Atom::ListRight },
+ { "Nop", Atom::Nop },
+ { "NoteLeft", Atom::NoteLeft },
+ { "NoteRight", Atom::NoteRight },
+ { "ParaLeft", Atom::ParaLeft },
+ { "ParaRight", Atom::ParaRight },
+ { "Qml", Atom::Qml},
+ { "QmlText", Atom::QmlText },
+ { "QuotationLeft", Atom::QuotationLeft },
+ { "QuotationRight", Atom::QuotationRight },
+ { "RawString", Atom::RawString },
+ { "SectionLeft", Atom::SectionLeft },
+ { "SectionRight", Atom::SectionRight },
+ { "SectionHeadingLeft", Atom::SectionHeadingLeft },
+ { "SectionHeadingRight", Atom::SectionHeadingRight },
+ { "SidebarLeft", Atom::SidebarLeft },
+ { "SidebarRight", Atom::SidebarRight },
+ { "SinceList", Atom::SinceList },
+ { "SnippetCommand", Atom::SnippetCommand },
+ { "SnippetIdentifier", Atom::SnippetIdentifier },
+ { "SnippetLocation", Atom::SnippetLocation },
+ { "String", Atom::String },
+ { "TableLeft", Atom::TableLeft },
+ { "TableRight", Atom::TableRight },
+ { "TableHeaderLeft", Atom::TableHeaderLeft },
+ { "TableHeaderRight", Atom::TableHeaderRight },
+ { "TableRowLeft", Atom::TableRowLeft },
+ { "TableRowRight", Atom::TableRowRight },
+ { "TableItemLeft", Atom::TableItemLeft },
+ { "TableItemRight", Atom::TableItemRight },
+ { "TableOfContents", Atom::TableOfContents },
+ { "Target", Atom::Target },
+ { "UnhandledFormat", Atom::UnhandledFormat },
+ { "UnknownCommand", Atom::UnknownCommand },
+ { 0, 0 }
+};
+
+/*! \fn Atom::Atom(Type type, const QString& string)
+
+ Constructs an atom of the specified \a type with the single
+ parameter \a string and does not put the new atom in a list.
+*/
+
+/*! \fn Atom::Atom(Type type, const QString& p1, const QString& p2)
+
+ Constructs an atom of the specified \a type with the two
+ parameters \a p1 and \a p2 and does not put the new atom
+ in a list.
+*/
+
+/*! \fn Atom(Atom *previous, Type type, const QString& string)
+
+ Constructs an atom of the specified \a type with the single
+ parameter \a string and inserts the new atom into the list
+ after the \a previous atom.
+*/
+
+/*! \fn Atom::Atom(Atom* previous, Type type, const QString& p1, const QString& p2)
+
+ Constructs an atom of the specified \a type with the two
+ parameters \a p1 and \a p2 and inserts the new atom into
+ the list after the \a previous atom.
+*/
+
+/*! \fn void Atom::appendChar(QChar ch)
+
+ Appends \a ch to the string parameter of this atom.
+
+ \also string()
+*/
+
+/*! \fn void Atom::appendString(const QString& string)
+
+ Appends \a string to the string parameter of this atom.
+
+ \also string()
+*/
+
+/*! \fn void Atom::chopString()
+
+ \also string()
+*/
+
+/*! \fn Atom *Atom::next()
+ Return the next atom in the atom list.
+ \also type(), string()
+*/
+
+/*!
+ Return the next Atom in the list if it is of Type \a t.
+ Otherwise return 0.
+ */
+const Atom* Atom::next(Type t) const
+{
+ return (next_ && (next_->type() == t)) ? next_ : 0;
+}
+
+/*!
+ Return the next Atom in the list if it is of Type \a t
+ and its string part is \a s. Otherwise return 0.
+ */
+const Atom* Atom::next(Type t, const QString& s) const
+{
+ return (next_ && (next_->type() == t) && (next_->string() == s)) ? next_ : 0;
+}
+
+/*! \fn const Atom *Atom::next() const
+ Return the next atom in the atom list.
+ \also type(), string()
+*/
+
+/*! \fn Type Atom::type() const
+ Return the type of this atom.
+ \also string(), next()
+*/
+
+/*!
+ Return the type of this atom as a string. Return "Invalid" if
+ type() returns an impossible value.
+
+ This is only useful for debugging.
+
+ \also type()
+*/
+QString Atom::typeString() const
+{
+ static bool deja = false;
+
+ if (!deja) {
+ int i = 0;
+ while (atms[i].english != 0) {
+ if (atms[i].no != i)
+ Location::internalError(tr("atom %1 missing").arg(i));
+ i++;
+ }
+ deja = true;
+ }
+
+ int i = (int) type();
+ if (i < 0 || i > (int) Last)
+ return QLatin1String("Invalid");
+ return QLatin1String(atms[i].english);
+}
+
+/*! \fn const QString& Atom::string() const
+
+ Returns the string parameter that together with the type
+ characterizes this atom.
+
+ \also type(), next()
+*/
+
+/*!
+ Dumps this Atom to stderr in printer friendly form.
+ */
+void Atom::dump() const
+{
+ QString str = string();
+ str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
+ str.replace(QLatin1String("\""), QLatin1String("\\\""));
+ str.replace(QLatin1String("\n"), QLatin1String("\\n"));
+ str.replace(QRegExp(QLatin1String("[^\x20-\x7e]")), QLatin1String("?"));
+ if (!str.isEmpty())
+ str = QLatin1String(" \"") + str + QLatin1String("\"");
+ fprintf(stderr,
+ " %-15s%s\n",
+ typeString().toLatin1().data(),
+ str.toLatin1().data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/atom.h b/src/tools/qdoc/atom.h
new file mode 100644
index 0000000..584e8fc
--- /dev/null
+++ b/src/tools/qdoc/atom.h
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ atom.h
+*/
+
+#ifndef ATOM_H
+#define ATOM_H
+
+#include
+
+#define QDOC_QML
+
+QT_BEGIN_NAMESPACE
+
+class Atom
+{
+public:
+ enum Type {
+ AbstractLeft,
+ AbstractRight,
+ AnnotatedList,
+ AutoLink,
+ BaseName,
+ BriefLeft,
+ BriefRight,
+ C,
+ CaptionLeft,
+ CaptionRight,
+ Code,
+ CodeBad,
+ CodeNew,
+ CodeOld,
+ CodeQuoteArgument,
+ CodeQuoteCommand,
+ DivLeft,
+ DivRight,
+ EndQmlText,
+ FootnoteLeft,
+ FootnoteRight,
+ FormatElse,
+ FormatEndif,
+ FormatIf,
+ FormattingLeft,
+ FormattingRight,
+ GeneratedList,
+ GuidLink,
+ Image,
+ ImageText,
+ ImportantLeft,
+ ImportantRight,
+ InlineImage,
+ JavaScript,
+ EndJavaScript,
+ LegaleseLeft,
+ LegaleseRight,
+ LineBreak,
+ Link,
+ LinkNode,
+ ListLeft,
+ ListItemNumber,
+ ListTagLeft,
+ ListTagRight,
+ ListItemLeft,
+ ListItemRight,
+ ListRight,
+ Nop,
+ NoteLeft,
+ NoteRight,
+ ParaLeft,
+ ParaRight,
+ Qml,
+ QmlText,
+ QuotationLeft,
+ QuotationRight,
+ RawString,
+ SectionLeft,
+ SectionRight,
+ SectionHeadingLeft,
+ SectionHeadingRight,
+ SidebarLeft,
+ SidebarRight,
+ SinceList,
+ SnippetCommand,
+ SnippetIdentifier,
+ SnippetLocation,
+ String,
+ TableLeft,
+ TableRight,
+ TableHeaderLeft,
+ TableHeaderRight,
+ TableRowLeft,
+ TableRowRight,
+ TableItemLeft,
+ TableItemRight,
+ TableOfContents,
+ Target,
+ UnhandledFormat,
+ UnknownCommand,
+ Last = UnknownCommand
+ };
+
+ Atom(Type type, const QString& string = "")
+ : next_(0), type_(type)
+ {
+ strs << string;
+ }
+
+ Atom(Type type, const QString& p1, const QString& p2)
+ : next_(0), type_(type)
+ {
+ strs << p1;
+ if (!p2.isEmpty())
+ strs << p2;
+ }
+
+ Atom(Atom* previous, Type type, const QString& string = "")
+ : next_(previous->next_), type_(type)
+ {
+ strs << string;
+ previous->next_ = this;
+ }
+
+ Atom(Atom* previous, Type type, const QString& p1, const QString& p2)
+ : next_(previous->next_), type_(type)
+ {
+ strs << p1;
+ if (!p2.isEmpty())
+ strs << p2;
+ previous->next_ = this;
+ }
+
+ void appendChar(QChar ch) { strs[0] += ch; }
+ void appendString(const QString& string) { strs[0] += string; }
+ void chopString() { strs[0].chop(1); }
+ void setString(const QString& string) { strs[0] = string; }
+ Atom* next() { return next_; }
+ void setNext(Atom* newNext) { next_ = newNext; }
+
+ const Atom* next() const { return next_; }
+ const Atom* next(Type t) const;
+ const Atom* next(Type t, const QString& s) const;
+ Type type() const { return type_; }
+ QString typeString() const;
+ const QString& string() const { return strs[0]; }
+ const QString& string(int i) const { return strs[i]; }
+ int count() const { return strs.size(); }
+ void dump() const;
+
+ static QLatin1String BOLD_;
+ static QLatin1String INDEX_;
+ static QLatin1String ITALIC_;
+ static QLatin1String LINK_;
+ static QLatin1String PARAMETER_;
+ static QLatin1String SPAN_;
+ static QLatin1String SUBSCRIPT_;
+ static QLatin1String SUPERSCRIPT_;
+ static QLatin1String TELETYPE_;
+ static QLatin1String UNDERLINE_;
+
+ static QLatin1String BULLET_;
+ static QLatin1String TAG_;
+ static QLatin1String VALUE_;
+ static QLatin1String LOWERALPHA_;
+ static QLatin1String LOWERROMAN_;
+ static QLatin1String NUMERIC_;
+ static QLatin1String UPPERALPHA_;
+ static QLatin1String UPPERROMAN_;
+
+private:
+ Atom* next_;
+ Type type_;
+ QStringList strs;
+};
+
+#define ATOM_FORMATTING_BOLD "bold"
+#define ATOM_FORMATTING_INDEX "index"
+#define ATOM_FORMATTING_ITALIC "italic"
+#define ATOM_FORMATTING_LINK "link"
+#define ATOM_FORMATTING_PARAMETER "parameter"
+#define ATOM_FORMATTING_SPAN "span "
+#define ATOM_FORMATTING_SUBSCRIPT "subscript"
+#define ATOM_FORMATTING_SUPERSCRIPT "superscript"
+#define ATOM_FORMATTING_TELETYPE "teletype"
+#define ATOM_FORMATTING_UNDERLINE "underline"
+
+#define ATOM_LIST_BULLET "bullet"
+#define ATOM_LIST_TAG "tag"
+#define ATOM_LIST_VALUE "value"
+#define ATOM_LIST_LOWERALPHA "loweralpha"
+#define ATOM_LIST_LOWERROMAN "lowerroman"
+#define ATOM_LIST_NUMERIC "numeric"
+#define ATOM_LIST_UPPERALPHA "upperalpha"
+#define ATOM_LIST_UPPERROMAN "upperroman"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/codechunk.cpp b/src/tools/qdoc/codechunk.cpp
new file mode 100644
index 0000000..a722bb1
--- /dev/null
+++ b/src/tools/qdoc/codechunk.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codechunk.cpp
+*/
+
+#include
+#include
+
+#include "codechunk.h"
+
+QT_BEGIN_NAMESPACE
+
+enum { Other, Alnum, Gizmo, Comma, LParen, RParen, RAngle, Colon };
+
+// entries 128 and above are Other
+static const int charCategory[256] = {
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ Other, Other, Other, Other, Other, Other, Other, Other,
+ // ! " # $ % & '
+ Other, Other, Other, Other, Other, Gizmo, Gizmo, Other,
+ // ( ) * + , - . /
+ LParen, RParen, Gizmo, Gizmo, Comma, Other, Other, Gizmo,
+ // 0 1 2 3 4 5 6 7
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // 8 9 : ; < = > ?
+ Alnum, Alnum, Colon, Other, Other, Gizmo, RAngle, Gizmo,
+ // @ A B C D E F G
+ Other, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // H I J K L M N O
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // P Q R S T U V W
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // X Y Z [ \ ] ^ _
+ Alnum, Alnum, Alnum, Other, Other, Other, Gizmo, Alnum,
+ // ` a b c d e f g
+ Other, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // h i j k l m n o
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // p q r s t u v w
+ Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
+ // x y z { | } ~
+ Alnum, Alnum, Alnum, LParen, Gizmo, RParen, Other, Other
+};
+
+static const bool needSpace[8][8] = {
+ /* [ a + , ( ) > : */
+ /* [ */ { false, false, false, false, false, true, false, false },
+ /* a */ { false, true, true, false, false, true, false, false },
+ /* + */ { false, true, false, false, false, true, false, true },
+ /* , */ { true, true, true, true, true, true, true, true },
+ /* ( */ { true, true, true, false, true, false, true, true },
+ /* ) */ { true, true, true, false, true, true, true, true },
+ /* > */ { true, true, true, false, true, true, true, false },
+ /* : */ { false, false, true, true, true, true, true, false }
+};
+
+static int category( QChar ch )
+{
+ return charCategory[(int)ch.toLatin1()];
+}
+
+CodeChunk::CodeChunk()
+ : hotspot( -1 )
+{
+}
+
+CodeChunk::CodeChunk( const QString& str )
+ : s( str ), hotspot( -1 )
+{
+}
+
+void CodeChunk::append( const QString& lexeme )
+{
+ if ( !s.isEmpty() && !lexeme.isEmpty() ) {
+ /*
+ Should there be a space or not between the code chunk so far and the
+ new lexeme?
+ */
+ int cat1 = category(s.at(s.size() - 1));
+ int cat2 = category(lexeme[0]);
+ if ( needSpace[cat1][cat2] )
+ s += QLatin1Char( ' ' );
+ }
+ s += lexeme;
+}
+
+void CodeChunk::appendHotspot()
+{
+ /*
+ The first hotspot is the right one.
+ */
+ if ( hotspot == -1 )
+ hotspot = s.length();
+}
+
+QString CodeChunk::toString() const
+{
+ return s;
+}
+
+QStringList CodeChunk::toPath() const
+{
+ QString t = s;
+ t.remove(QRegExp(QLatin1String("<([^<>]|<([^<>]|<[^<>]*>)*>)*>")));
+ return t.split(QLatin1String("::"));
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/codechunk.h b/src/tools/qdoc/codechunk.h
new file mode 100644
index 0000000..ee30f85
--- /dev/null
+++ b/src/tools/qdoc/codechunk.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codechunk.h
+*/
+
+#ifndef CODECHUNK_H
+#define CODECHUNK_H
+
+#include
+
+QT_BEGIN_NAMESPACE
+
+// ### get rid of that class
+
+/*
+ The CodeChunk class represents a tiny piece of C++ code.
+
+ The class provides conversion between a list of lexemes and a string. It adds
+ spaces at the right place for consistent style. The tiny pieces of code it
+ represents are data types, enum values, and default parameter values.
+
+ Apart from the piece of code itself, there are two bits of metainformation
+ stored in CodeChunk: the base and the hotspot. The base is the part of the
+ piece that may be a hypertext link. The base of
+
+ QMap
+
+ is QMap.
+
+ The hotspot is the place the variable name should be inserted in the case of a
+ variable (or parameter) declaration. The base of
+
+ char * []
+
+ is between '*' and '[]'.
+*/
+class CodeChunk
+{
+public:
+ CodeChunk();
+ CodeChunk( const QString& str );
+
+ void append( const QString& lexeme );
+ void appendHotspot();
+
+ bool isEmpty() const { return s.isEmpty(); }
+ QString toString() const;
+ QStringList toPath() const;
+ QString left() const { return s.left(hotspot == -1 ? s.length() : hotspot); }
+ QString right() const { return s.mid(hotspot == -1 ? s.length() : hotspot); }
+
+private:
+ QString s;
+ int hotspot;
+};
+
+inline bool operator==( const CodeChunk& c, const CodeChunk& d ) {
+ return c.toString() == d.toString();
+}
+
+inline bool operator!=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c == d );
+}
+
+inline bool operator<( const CodeChunk& c, const CodeChunk& d ) {
+ return c.toString() < d.toString();
+}
+
+inline bool operator>( const CodeChunk& c, const CodeChunk& d ) {
+ return d < c;
+}
+
+inline bool operator<=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c > d );
+}
+
+inline bool operator>=( const CodeChunk& c, const CodeChunk& d ) {
+ return !( c < d );
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/codemarker.cpp b/src/tools/qdoc/codemarker.cpp
new file mode 100644
index 0000000..791e080
--- /dev/null
+++ b/src/tools/qdoc/codemarker.cpp
@@ -0,0 +1,682 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include
+#include "codemarker.h"
+#include "config.h"
+#include "node.h"
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+QString CodeMarker::defaultLang;
+QList CodeMarker::markers;
+
+/*!
+ When a code marker constructs itself, it puts itself into
+ the static list of code markers. All the code markers in
+ the static list get initialized in initialize(), which is
+ not called until after the qdoc configuration file has
+ been read.
+ */
+CodeMarker::CodeMarker()
+{
+ markers.prepend(this);
+}
+
+/*!
+ When a code marker destroys itself, it removes itself from
+ the static list of code markers.
+ */
+CodeMarker::~CodeMarker()
+{
+ markers.removeAll(this);
+}
+
+/*!
+ A code market performs no initialization by default. Marker-specific
+ initialization is performed in subclasses.
+ */
+void CodeMarker::initializeMarker(const Config& ) // config
+{
+}
+
+/*!
+ Terminating a code marker is trivial.
+ */
+void CodeMarker::terminateMarker()
+{
+ // nothing.
+}
+
+/*!
+ All the code markers in the static list are initialized
+ here, after the qdoc configuration file has been loaded.
+ */
+void CodeMarker::initialize(const Config& config)
+{
+ defaultLang = config.getString(QLatin1String(CONFIG_LANGUAGE));
+ QList::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ (*m)->initializeMarker(config);
+ ++m;
+ }
+}
+
+/*!
+ All the code markers in the static list are terminated here.
+ */
+void CodeMarker::terminate()
+{
+ QList::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ (*m)->terminateMarker();
+ ++m;
+ }
+}
+
+CodeMarker *CodeMarker::markerForCode(const QString& code)
+{
+ CodeMarker *defaultMarker = markerForLanguage(defaultLang);
+ if (defaultMarker != 0 && defaultMarker->recognizeCode(code))
+ return defaultMarker;
+
+ QList::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeCode(code))
+ return *m;
+ ++m;
+ }
+ return defaultMarker;
+}
+
+CodeMarker *CodeMarker::markerForFileName(const QString& fileName)
+{
+ CodeMarker *defaultMarker = markerForLanguage(defaultLang);
+ int dot = -1;
+ while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
+ QString ext = fileName.mid(dot + 1);
+ if (defaultMarker != 0 && defaultMarker->recognizeExtension(ext))
+ return defaultMarker;
+ QList::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeExtension(ext))
+ return *m;
+ ++m;
+ }
+ --dot;
+ }
+ return defaultMarker;
+}
+
+CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
+{
+ QList::ConstIterator m = markers.begin();
+ while (m != markers.end()) {
+ if ((*m)->recognizeLanguage(lang))
+ return *m;
+ ++m;
+ }
+ return 0;
+}
+
+const Node *CodeMarker::nodeForString(const QString& string)
+{
+ if (sizeof(const Node *) == sizeof(uint)) {
+ return reinterpret_cast(string.toUInt());
+ }
+ else {
+ return reinterpret_cast(string.toULongLong());
+ }
+}
+
+QString CodeMarker::stringForNode(const Node *node)
+{
+ if (sizeof(const Node *) == sizeof(ulong)) {
+ return QString::number(reinterpret_cast(node));
+ }
+ else {
+ return QString::number(reinterpret_cast(node));
+ }
+}
+
+static const QString samp = QLatin1String("&");
+static const QString slt = QLatin1String("<");
+static const QString sgt = QLatin1String(">");
+static const QString squot = QLatin1String(""");
+
+QString CodeMarker::protect(const QString& str)
+{
+ int n = str.length();
+ QString marked;
+ marked.reserve(n * 2 + 30);
+ const QChar *data = str.constData();
+ for (int i = 0; i != n; ++i) {
+ switch (data[i].unicode()) {
+ case '&': marked += samp; break;
+ case '<': marked += slt; break;
+ case '>': marked += sgt; break;
+ case '"': marked += squot; break;
+ default : marked += data[i];
+ }
+ }
+ return marked;
+}
+
+QString CodeMarker::typified(const QString &string)
+{
+ QString result;
+ QString pendingWord;
+
+ 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(':')) {
+ pendingWord += ch;
+ }
+ else {
+ if (!pendingWord.isEmpty()) {
+ bool isProbablyType = (pendingWord != QLatin1String("const"));
+ if (isProbablyType)
+ result += QLatin1String("<@type>");
+ result += pendingWord;
+ if (isProbablyType)
+ result += QLatin1String("@type>");
+ }
+ pendingWord.clear();
+
+ switch (ch.unicode()) {
+ case '\0':
+ break;
+ case '&':
+ result += QLatin1String("&");
+ break;
+ case '<':
+ result += QLatin1String("<");
+ break;
+ case '>':
+ result += QLatin1String(">");
+ break;
+ default:
+ result += ch;
+ }
+ }
+ }
+ return result;
+}
+
+QString CodeMarker::taggedNode(const Node* node)
+{
+ QString tag;
+ QString name = node->name();
+
+ switch (node->type()) {
+ case Node::Namespace:
+ tag = QLatin1String("@namespace");
+ break;
+ case Node::Class:
+ tag = QLatin1String("@class");
+ break;
+ case Node::Enum:
+ tag = QLatin1String("@enum");
+ break;
+ case Node::Typedef:
+ tag = QLatin1String("@typedef");
+ break;
+ case Node::Function:
+ tag = QLatin1String("@function");
+ break;
+ case Node::Property:
+ tag = QLatin1String("@property");
+ break;
+ case Node::Fake:
+ /*
+ Remove the "QML:" prefix, if present.
+ There shouldn't be any of these "QML:"
+ prefixes in the documentation sources
+ after the switch to using QML module
+ qualifiers, but this code is kept to
+ be backward compatible.
+ */
+ if (node->subType() == Node::QmlClass) {
+ if (node->name().startsWith(QLatin1String("QML:")))
+ name = name.mid(4);
+ }
+ tag = QLatin1String("@property");
+ break;
+ default:
+ tag = QLatin1String("@unknown");
+ break;
+ }
+ return QLatin1Char('<') + tag + QLatin1Char('>') + protect(name)
+ + QLatin1String("") + tag + QLatin1Char('>');
+}
+
+QString CodeMarker::taggedQmlNode(const Node* node)
+{
+ QString tag;
+ switch (node->type()) {
+ case Node::QmlProperty:
+ tag = QLatin1String("@property");
+ break;
+ case Node::QmlSignal:
+ tag = QLatin1String("@signal");
+ break;
+ case Node::QmlSignalHandler:
+ tag = QLatin1String("@signalhandler");
+ break;
+ case Node::QmlMethod:
+ tag = QLatin1String("@method");
+ break;
+ default:
+ tag = QLatin1String("@unknown");
+ break;
+ }
+ return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
+ + QLatin1String("") + tag + QLatin1Char('>');
+}
+
+QString CodeMarker::linkTag(const Node *node, const QString& body)
+{
+ return QLatin1String("<@link node=\"") + stringForNode(node)
+ + QLatin1String("\">") + body + QLatin1String("@link>");
+}
+
+QString CodeMarker::sortName(const Node *node, const QString* name)
+{
+ QString nodeName;
+ if (name != 0)
+ nodeName = *name;
+ else
+ nodeName = node->name();
+ int numDigits = 0;
+ for (int i = nodeName.size() - 1; i > 0; --i) {
+ if (nodeName.at(i).digitValue() == -1)
+ break;
+ ++numDigits;
+ }
+
+ // we want 'qint8' to appear before 'qint16'
+ if (numDigits > 0) {
+ for (int i = 0; i < 4 - numDigits; ++i)
+ nodeName.insert(nodeName.size()-numDigits-1, QLatin1Char('0'));
+ }
+
+ if (node->type() == Node::Function) {
+ const FunctionNode *func = static_cast(node);
+ QString sortNo;
+ if (func->metaness() == FunctionNode::Ctor) {
+ sortNo = QLatin1String("C");
+ }
+ else if (func->metaness() == FunctionNode::Dtor) {
+ sortNo = QLatin1String("D");
+ }
+ else {
+ if (nodeName.startsWith(QLatin1String("operator"))
+ && nodeName.length() > 8
+ && !nodeName[8].isLetterOrNumber())
+ sortNo = QLatin1String("F");
+ else
+ sortNo = QLatin1String("E");
+ }
+ return sortNo + nodeName + QLatin1Char(' ')
+ + QString::number(func->overloadNumber(), 36);
+ }
+
+ if (node->type() == Node::Class)
+ return QLatin1Char('A') + nodeName;
+
+ if (node->type() == Node::Property || node->type() == Node::Variable)
+ return QLatin1Char('E') + nodeName;
+
+ return QLatin1Char('B') + nodeName;
+}
+
+void CodeMarker::insert(FastSection &fastSection,
+ Node *node,
+ SynopsisStyle style,
+ Status status)
+{
+ bool irrelevant = false;
+ bool inheritedMember = false;
+ if (!node->relates()) {
+ if (node->parent() != (const InnerNode*)fastSection.innerNode && !node->parent()->isAbstract()) {
+ if (node->type() != Node::QmlProperty)
+ inheritedMember = true;
+ }
+ }
+
+ if (node->access() == Node::Private) {
+ irrelevant = true;
+ }
+ else if (node->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) node;
+ irrelevant = (inheritedMember
+ && (func->metaness() == FunctionNode::Ctor ||
+ func->metaness() == FunctionNode::Dtor));
+ }
+ else if (node->type() == Node::Class || node->type() == Node::Enum
+ || node->type() == Node::Typedef) {
+ irrelevant = (inheritedMember && style != Subpage);
+ if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
+ const TypedefNode* typedeffe = static_cast(node);
+ if (typedeffe->associatedEnum())
+ irrelevant = true;
+ }
+ }
+
+ if (!irrelevant) {
+ if (status == Compat) {
+ irrelevant = (node->status() != Node::Compat);
+ }
+ else if (status == Obsolete) {
+ irrelevant = (node->status() != Node::Obsolete);
+ }
+ else {
+ irrelevant = (node->status() == Node::Compat ||
+ node->status() == Node::Obsolete);
+ }
+ }
+
+ if (!irrelevant) {
+ if (!inheritedMember || style == Subpage) {
+ QString key = sortName(node);
+ if (!fastSection.memberMap.contains(key))
+ fastSection.memberMap.insert(key, node);
+ }
+ else {
+ if (node->parent()->type() == Node::Class) {
+ if (fastSection.inherited.isEmpty()
+ || fastSection.inherited.last().first != node->parent()) {
+ QPair p(node->parent(), 0);
+ fastSection.inherited.append(p);
+ }
+ fastSection.inherited.last().second++;
+ }
+ }
+ }
+}
+
+void CodeMarker::insert(FastSection& fastSection,
+ Node* node,
+ SynopsisStyle style,
+ bool /* includeClassName */)
+{
+ if (node->status() == Node::Compat || node->status() == Node::Obsolete)
+ return;
+
+ bool inheritedMember = false;
+ InnerNode* parent = node->parent();
+ if (parent && (parent->type() == Node::Fake) &&
+ (parent->subType() == Node::QmlPropertyGroup)) {
+ parent = parent->parent();
+ }
+ inheritedMember = (parent != (const InnerNode*)fastSection.innerNode);
+
+ if (!inheritedMember || style == Subpage) {
+ QString key = sortName(node);
+ if (!fastSection.memberMap.contains(key))
+ fastSection.memberMap.insert(key, node);
+ }
+ else {
+ if ((parent->type() == Node::Fake) && (parent->subType() == Node::QmlClass)) {
+ if (fastSection.inherited.isEmpty()
+ || fastSection.inherited.last().first != parent) {
+ QPair p(parent, 0);
+ fastSection.inherited.append(p);
+ }
+ fastSection.inherited.last().second++;
+ }
+ }
+}
+
+/*!
+ Returns true if \a node represents a reimplemented member function.
+ If it is, then it is inserted in the reimplemented member map in the
+ section \a fs. And, the test is only performed if \a status is \e OK.
+ Otherwise, false is returned.
+ */
+bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
+{
+ if (node->access() == Node::Private)
+ return false;
+
+ const FunctionNode* fn = static_cast(node);
+ if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
+ bool inherited = (!fn->relates() && (fn->parent() != (const InnerNode*)fs.innerNode));
+ if (!inherited) {
+ QString key = sortName(fn);
+ if (!fs.reimpMemberMap.contains(key)) {
+ fs.reimpMemberMap.insert(key,node);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*!
+ If \a fs is not empty, convert it to a Section and append
+ the new Section to \a sectionList.
+ */
+void CodeMarker::append(QList& sectionList, const FastSection& fs, bool includeKeys)
+{
+ if (!fs.isEmpty()) {
+ Section section(fs.name,fs.divClass,fs.singularMember,fs.pluralMember);
+ if (includeKeys) {
+ section.keys = fs.memberMap.keys();
+ }
+ section.members = fs.memberMap.values();
+ section.reimpMembers = fs.reimpMemberMap.values();
+ section.inherited = fs.inherited;
+ sectionList.append(section);
+ }
+}
+
+static QString encode(const QString &string)
+{
+#if 0
+ QString result = string;
+
+ for (int i = string.size() - 1; i >= 0; --i) {
+ uint ch = string.at(i).unicode();
+ if (ch > 0xFF)
+ ch = '?';
+ if ((ch - '0') >= 10 && (ch - 'a') >= 26 && (ch - 'A') >= 26
+ && ch != '/' && ch != '(' && ch != ')' && ch != ',' && ch != '*'
+ && ch != '&' && ch != '_' && ch != '<' && ch != '>' && ch != ':'
+ && ch != '~')
+ result.replace(i, 1, QString("%") + QString("%1").arg(ch, 2, 16));
+ }
+ return result;
+#else
+ return string;
+#endif
+}
+
+QStringList CodeMarker::macRefsForNode(Node *node)
+{
+ QString result = QLatin1String("cpp/");
+ switch (node->type()) {
+ case Node::Class:
+ {
+ const ClassNode *classe = static_cast(node);
+#if 0
+ if (!classe->templateStuff().isEmpty()) {
+ result += QLatin1String("tmplt/");
+ }
+ else
+#endif
+ {
+ result += QLatin1String("cl/");
+ }
+ result += macName(classe); // ### Maybe plainName?
+ }
+ break;
+ case Node::Enum:
+ {
+ QStringList stringList;
+ stringList << encode(result + QLatin1String("tag/") +
+ macName(node));
+ foreach (const QString &enumName, node->doc().enumItemNames()) {
+ // ### Write a plainEnumValue() and use it here
+ stringList << encode(result + QLatin1String("econst/") +
+ macName(node->parent(), enumName));
+ }
+ return stringList;
+ }
+ case Node::Typedef:
+ result += QLatin1String("tdef/") + macName(node);
+ break;
+ case Node::Function:
+ {
+ bool isMacro = false;
+ Q_UNUSED(isMacro)
+ const FunctionNode *func = static_cast(node);
+
+ // overloads are too clever for the Xcode documentation browser
+ if (func->isOverload())
+ return QStringList();
+
+ if (func->metaness() == FunctionNode::MacroWithParams
+ || func->metaness() == FunctionNode::MacroWithoutParams) {
+ result += QLatin1String("macro/");
+#if 0
+ }
+ else if (!func->templateStuff().isEmpty()) {
+ result += QLatin1String("ftmplt/");
+#endif
+ }
+ else if (func->isStatic()) {
+ result += QLatin1String("clm/");
+ }
+ else if (!func->parent()->name().isEmpty()) {
+ result += QLatin1String("instm/");
+ }
+ else {
+ result += QLatin1String("func/");
+ }
+
+ result += macName(func);
+ if (result.endsWith(QLatin1String("()")))
+ result.chop(2);
+#if 0
+ // this code is too clever for the Xcode documentation
+ // browser and/or pbhelpindexer
+ if (!isMacro) {
+ result += QLatin1Char('/') + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/(";
+ const QList ¶ms = func->parameters();
+ for (int i = 0; i < params.count(); ++i) {
+ QString type = params.at(i).leftType() +
+ params.at(i).rightType();
+ type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData()));
+ if (i != 0)
+ result += QLatin1Char(',');
+ result += type;
+ }
+ result += QLatin1Char(')');
+ }
+#endif
+ }
+ break;
+ case Node::Variable:
+ result += QLatin1String("data/") + macName(node);
+ break;
+ case Node::Property:
+ {
+ NodeList list = static_cast(node)->functions();
+ QStringList stringList;
+ foreach (Node* node, list) {
+ stringList += macRefsForNode(node);
+ }
+ return stringList;
+ }
+ case Node::Namespace:
+ case Node::Fake:
+ case Node::Target:
+ default:
+ return QStringList();
+ }
+
+ return QStringList(encode(result));
+}
+
+QString CodeMarker::macName(const Node *node, const QString &name)
+{
+ QString myName = name;
+ if (myName.isEmpty()) {
+ myName = node->name();
+ node = node->parent();
+ }
+
+ if (node->name().isEmpty()) {
+ return QLatin1Char('/') + protect(myName);
+ }
+ else {
+ return plainFullName(node) + QLatin1Char('/') + protect(myName);
+ }
+}
+
+/*!
+ Get the list of documentation sections for the children of
+ the specified QmlClassNode.
+ */
+QList CodeMarker::qmlSections(const QmlClassNode* ,
+ SynopsisStyle )
+{
+ return QList();
+}
+
+const Node* CodeMarker::resolveTarget(const QString& ,
+ const Tree* ,
+ const Node* ,
+ const Node* )
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/codemarker.h b/src/tools/qdoc/codemarker.h
new file mode 100644
index 0000000..894d838
--- /dev/null
+++ b/src/tools/qdoc/codemarker.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codemarker.h
+*/
+
+#ifndef CODEMARKER_H
+#define CODEMARKER_H
+
+#include
+
+#include "atom.h"
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Tree;
+
+struct Section
+{
+ QString name;
+ QString divClass;
+ QString singularMember;
+ QString pluralMember;
+ QStringList keys;
+ NodeList members;
+ NodeList reimpMembers;
+ QList > inherited;
+
+ Section() { }
+ Section(const QString& name0,
+ const QString& divClass0,
+ const QString& singularMember0,
+ const QString& pluralMember0)
+ : name(name0),
+ divClass(divClass0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+ void appendMember(Node* node) { members.append(node); }
+ void appendReimpMember(Node* node) { reimpMembers.append(node); }
+};
+
+struct FastSection
+{
+ const InnerNode *innerNode;
+ QString name;
+ QString divClass;
+ QString singularMember;
+ QString pluralMember;
+ QMap memberMap;
+ QMap reimpMemberMap;
+ QList > inherited;
+
+ FastSection(const InnerNode *innerNode0,
+ const QString& name0,
+ const QString& divClass0,
+ const QString& singularMember0,
+ const QString& pluralMember0)
+ : innerNode(innerNode0),
+ name(name0),
+ divClass(divClass0),
+ singularMember(singularMember0),
+ pluralMember(pluralMember0) { }
+ bool isEmpty() const {
+ return (memberMap.isEmpty() &&
+ inherited.isEmpty() &&
+ reimpMemberMap.isEmpty());
+ }
+
+};
+
+class CodeMarker
+{
+public:
+ enum SynopsisStyle { Summary, Detailed, Subpage, Accessors };
+ enum Status { Compat, Obsolete, Okay };
+
+ CodeMarker();
+ virtual ~CodeMarker();
+
+ virtual void initializeMarker(const Config& config);
+ virtual void terminateMarker();
+ virtual bool recognizeCode(const QString& code) = 0;
+ virtual bool recognizeExtension(const QString& ext) = 0;
+ virtual bool recognizeLanguage(const QString& lang) = 0;
+ virtual Atom::Type atomType() const = 0;
+ virtual QString plainName(const Node *node) = 0;
+ virtual QString plainFullName(const Node *node,
+ const Node *relative = 0) = 0;
+ virtual QString markedUpCode(const QString& code,
+ const Node *relative,
+ const Location &location) = 0;
+ virtual QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style) = 0;
+ virtual QString markedUpQmlItem(const Node* , bool) { return QString(); }
+ virtual QString markedUpName(const Node *node) = 0;
+ virtual QString markedUpFullName(const Node *node,
+ const Node *relative = 0) = 0;
+ virtual QString markedUpEnumValue(const QString &enumValue,
+ const Node *relative) = 0;
+ virtual QString markedUpIncludes(const QStringList& includes) = 0;
+ virtual QString functionBeginRegExp(const QString& funcName) = 0;
+ virtual QString functionEndRegExp(const QString& funcName) = 0;
+ virtual QList sections(const InnerNode *inner,
+ SynopsisStyle style,
+ Status status) = 0;
+ virtual QList qmlSections(const QmlClassNode* qmlClassNode,
+ SynopsisStyle style);
+ virtual const Node* resolveTarget(const QString& target,
+ const Tree* tree,
+ const Node* relative,
+ const Node* self = 0);
+ virtual QStringList macRefsForNode(Node* node);
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static CodeMarker *markerForCode(const QString& code);
+ static CodeMarker *markerForFileName(const QString& fileName);
+ static CodeMarker *markerForLanguage(const QString& lang);
+ static const Node *nodeForString(const QString& string);
+ static QString stringForNode(const Node *node);
+
+ QString typified(const QString &string);
+
+protected:
+ virtual QString sortName(const Node *node, const QString* name = 0);
+ QString protect(const QString &string);
+ QString taggedNode(const Node* node);
+ QString taggedQmlNode(const Node* node);
+ QString linkTag(const Node *node, const QString& body);
+ void insert(FastSection &fastSection,
+ Node *node,
+ SynopsisStyle style,
+ Status status);
+ void insert(FastSection& fastSection,
+ Node* node,
+ SynopsisStyle style,
+ bool includeClassName = false);
+ bool insertReimpFunc(FastSection& fs, Node* node, Status status);
+ void append(QList& sectionList, const FastSection& fastSection, bool includeKeys = false);
+
+private:
+ QString macName(const Node *parent, const QString &name = QString());
+
+ static QString defaultLang;
+ static QList markers;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp
new file mode 100644
index 0000000..5173a40
--- /dev/null
+++ b/src/tools/qdoc/codeparser.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codeparser.cpp
+*/
+
+#include "codeparser.h"
+#include "node.h"
+#include "tree.h"
+#include "config.h"
+#include "generator.h"
+#include
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_COMPAT Doc::alias(QLatin1String("compat"))
+#define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated")) // ### don't document
+#define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup"))
+#define COMMAND_INMODULE Doc::alias(QLatin1String("inmodule")) // ### don't document
+#define COMMAND_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule"))
+#define COMMAND_INTERNAL Doc::alias(QLatin1String("internal"))
+#define COMMAND_MAINCLASS Doc::alias(QLatin1String("mainclass"))
+#define COMMAND_NONREENTRANT Doc::alias(QLatin1String("nonreentrant"))
+#define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete"))
+#define COMMAND_PAGEKEYWORDS Doc::alias(QLatin1String("pagekeywords"))
+#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
+#define COMMAND_INPUBLICGROUP Doc::alias(QLatin1String("inpublicgroup"))
+#define COMMAND_REENTRANT Doc::alias(QLatin1String("reentrant"))
+#define COMMAND_SINCE Doc::alias(QLatin1String("since"))
+#define COMMAND_SUBTITLE Doc::alias(QLatin1String("subtitle"))
+#define COMMAND_THREADSAFE Doc::alias(QLatin1String("threadsafe"))
+#define COMMAND_TITLE Doc::alias(QLatin1String("title"))
+
+QString CodeParser::currentSubDir_;
+QList CodeParser::parsers;
+bool CodeParser::showInternal = false;
+QMap CodeParser::nameToTitle;
+
+/*!
+ The constructor adds this code parser to the static
+ list of code parsers.
+ */
+CodeParser::CodeParser()
+{
+ parsers.prepend(this);
+}
+
+/*!
+ The destructor removes this code parser from the static
+ list of code parsers.
+ */
+CodeParser::~CodeParser()
+{
+ parsers.removeAll(this);
+}
+
+/*!
+ Initialize the code parser base class.
+ */
+void CodeParser::initializeParser(const Config& config)
+{
+ showInternal = config.getBool(QLatin1String(CONFIG_SHOWINTERNAL));
+}
+
+/*!
+ Terminating a code parser is trivial.
+ */
+void CodeParser::terminateParser()
+{
+ // nothing.
+}
+
+QStringList CodeParser::headerFileNameFilter()
+{
+ return sourceFileNameFilter();
+}
+
+void CodeParser::parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ parseSourceFile(location, filePath, tree);
+}
+
+void CodeParser::doneParsingHeaderFiles(Tree *tree)
+{
+ doneParsingSourceFiles(tree);
+}
+
+/*!
+ All the code parsers in the static list are initialized here,
+ after the qdoc configuration variables have been set.
+ */
+void CodeParser::initialize(const Config& config)
+{
+ QList::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ (*p)->initializeParser(config);
+ ++p;
+ }
+}
+
+/*!
+ All the code parsers in the static list are terminated here.
+ */
+void CodeParser::terminate()
+{
+ QList::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ (*p)->terminateParser();
+ ++p;
+ }
+}
+
+CodeParser *CodeParser::parserForLanguage(const QString& language)
+{
+ QList::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+ if ((*p)->language() == language)
+ return *p;
+ ++p;
+ }
+ return 0;
+}
+
+CodeParser *CodeParser::parserForHeaderFile(const QString &filePath)
+{
+ QString fileName = QFileInfo(filePath).fileName();
+
+ QList::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+
+ QStringList headerPatterns = (*p)->headerFileNameFilter();
+ foreach (QString pattern, headerPatterns) {
+ QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (re.exactMatch(fileName))
+ return *p;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
+{
+ QString fileName = QFileInfo(filePath).fileName();
+
+ QList::ConstIterator p = parsers.begin();
+ while (p != parsers.end()) {
+
+ QStringList sourcePatterns = (*p)->sourceFileNameFilter();
+ foreach (QString pattern, sourcePatterns) {
+ QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (re.exactMatch(fileName))
+ return *p;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+/*!
+ Returns the set of strings representing the common metacommands.
+ */
+QSet CodeParser::commonMetaCommands()
+{
+ return QSet() << COMMAND_COMPAT
+ << COMMAND_DEPRECATED
+ << COMMAND_INGROUP
+ << COMMAND_INMODULE
+ << COMMAND_INQMLMODULE
+ << COMMAND_INTERNAL
+ << COMMAND_MAINCLASS
+ << COMMAND_NONREENTRANT
+ << COMMAND_OBSOLETE
+ << COMMAND_PAGEKEYWORDS
+ << COMMAND_PRELIMINARY
+ << COMMAND_INPUBLICGROUP
+ << COMMAND_REENTRANT
+ << COMMAND_SINCE
+ << COMMAND_SUBTITLE
+ << COMMAND_THREADSAFE
+ << COMMAND_TITLE;
+}
+
+/*!
+ The topic command has been processed. Now process the other
+ metacommands that were found. These are not the text markup
+ commands.
+ */
+void CodeParser::processCommonMetaCommand(const Location& location,
+ const QString& command,
+ const QString& arg,
+ Node* node,
+ Tree* tree)
+{
+ if (command == COMMAND_COMPAT) {
+ location.warning(tr("\\compat command used, but Qt3 compatibility is no longer supported"));
+ node->setStatus(Node::Compat);
+ }
+ else if (command == COMMAND_DEPRECATED) {
+ node->setStatus(Node::Deprecated);
+ }
+ else if (command == COMMAND_INGROUP) {
+ tree->addToGroup(node, arg);
+ }
+ else if (command == COMMAND_INPUBLICGROUP) {
+ tree->addToPublicGroup(node, arg);
+ }
+ else if (command == COMMAND_INMODULE) {
+ node->setModuleName(arg);
+ }
+ else if (command == COMMAND_INQMLMODULE) {
+ node->setQmlModuleName(arg);
+ tree->addToQmlModule(node,arg);
+ QString qmid = node->qmlModuleIdentifier();
+ QmlClassNode* qcn = static_cast(node);
+ QmlClassNode::moduleMap.insert(qmid + QLatin1String("::") + node->name(), qcn);
+ }
+ else if (command == COMMAND_MAINCLASS) {
+ node->setStatus(Node::Main);
+ }
+ else if (command == COMMAND_OBSOLETE) {
+ if (node->status() != Node::Compat)
+ node->setStatus(Node::Obsolete);
+ }
+ else if (command == COMMAND_NONREENTRANT) {
+ node->setThreadSafeness(Node::NonReentrant);
+ }
+ else if (command == COMMAND_PRELIMINARY) {
+ node->setStatus(Node::Preliminary);
+ }
+ else if (command == COMMAND_INTERNAL) {
+ if (!showInternal) {
+ node->setAccess(Node::Private);
+ node->setStatus(Node::Internal);
+ }
+ }
+ else if (command == COMMAND_REENTRANT) {
+ node->setThreadSafeness(Node::Reentrant);
+ }
+ else if (command == COMMAND_SINCE) {
+ node->setSince(arg);
+ }
+ else if (command == COMMAND_PAGEKEYWORDS) {
+ node->addPageKeywords(arg);
+ }
+ else if (command == COMMAND_SUBTITLE) {
+ if (node->type() == Node::Fake) {
+ FakeNode *fake = static_cast(node);
+ fake->setSubTitle(arg);
+ }
+ else
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
+ }
+ else if (command == COMMAND_THREADSAFE) {
+ node->setThreadSafeness(Node::ThreadSafe);
+ }
+ else if (command == COMMAND_TITLE) {
+ if (node->type() == Node::Fake) {
+ FakeNode *fake = static_cast(node);
+ fake->setTitle(arg);
+ if (fake->subType() == Node::Example) {
+ ExampleNode::exampleNodeMap.insert(fake->title(),static_cast(fake));
+ }
+ nameToTitle.insert(fake->name(),arg);
+ }
+ else
+ location.warning(tr("Ignored '\\%1'").arg(COMMAND_TITLE));
+ }
+}
+
+/*!
+ Find the page title given the page \a name and return it.
+ */
+const QString CodeParser::titleFromName(const QString& name)
+{
+ const QString t = nameToTitle.value(name);
+ return t;
+}
+
+/*!
+ \internal
+ */
+void CodeParser::extractPageLinkAndDesc(const QString& arg,
+ QString* link,
+ QString* desc)
+{
+ QRegExp bracedRegExp(QLatin1String("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?"));
+
+ if (bracedRegExp.exactMatch(arg)) {
+ *link = bracedRegExp.cap(1);
+ *desc = bracedRegExp.cap(2);
+ if (desc->isEmpty())
+ *desc = *link;
+ }
+ else {
+ int spaceAt = arg.indexOf(QLatin1Char(' '));
+ if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
+ *link = arg.left(spaceAt).trimmed();
+ *desc = arg.mid(spaceAt).trimmed();
+ }
+ else {
+ *link = arg;
+ *desc = arg;
+ }
+ }
+}
+
+/*!
+ \internal
+ */
+void CodeParser::setLink(Node* node, Node::LinkType linkType, const QString& arg)
+{
+ QString link;
+ QString desc;
+ extractPageLinkAndDesc(arg, &link, &desc);
+ node->setLink(linkType, link, desc);
+}
+
+/*!
+ If the \e {basedir} variable is not set in the qdocconf
+ file, do nothing.
+
+ Otherwise, search for the basedir string string in the
+ \a filePath. It must be found, or else a warning message
+ is output. Extract the subdirectory name that follows the
+ basedir name and create a subdirectory using that name
+ in the output director.
+ */
+void CodeParser::createOutputSubdirectory(const Location& location,
+ const QString& filePath)
+{
+ QString bd = Generator::baseDir();
+ if (!bd.isEmpty()) {
+ int baseIdx = filePath.indexOf(bd);
+ if (baseIdx == -1)
+ location.warning(tr("File path: '%1' does not contain bundle base dir: '%2'")
+ .arg(filePath).arg(bd));
+ else {
+ int subDirIdx = filePath.indexOf(QLatin1Char('/'),baseIdx);
+ if (subDirIdx == -1)
+ location.warning(tr("File path: '%1' has no sub dir after bundle base dir: '%2'")
+ .arg(filePath).arg(bd));
+ else {
+ ++subDirIdx;
+ int fileNameIdx = filePath.indexOf(QLatin1Char('/'),subDirIdx);
+ if (fileNameIdx == -1)
+ location.warning(tr("File path: '%1' has no file name after sub dir: '%2/'")
+ .arg(filePath).arg(filePath.mid(subDirIdx)));
+ else {
+ currentSubDir_ = filePath.mid(subDirIdx,fileNameIdx-subDirIdx);
+ if (currentSubDir_.isEmpty())
+ location.warning(tr("File path: '%1' has no sub dir after bundle base dir: '%2'")
+ .arg(filePath).arg(bd));
+ else {
+ QString subDirPath = Generator::outputDir() + QLatin1Char('/') + currentSubDir_;
+ QDir dirInfo;
+ if (!dirInfo.exists(subDirPath)) {
+ if (!dirInfo.mkpath(subDirPath))
+ location.fatal(tr("Cannot create output sub-directory '%1'").arg(currentSubDir_));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/codeparser.h b/src/tools/qdoc/codeparser.h
new file mode 100644
index 0000000..f522567
--- /dev/null
+++ b/src/tools/qdoc/codeparser.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ codeparser.h
+*/
+
+#ifndef CODEPARSER_H
+#define CODEPARSER_H
+
+#include
+
+#include "node.h"
+
+QT_BEGIN_NAMESPACE
+
+class Config;
+class Node;
+class QString;
+class Tree;
+
+class CodeParser
+{
+public:
+ CodeParser();
+ virtual ~CodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language() = 0;
+ virtual QStringList headerFileNameFilter();
+ virtual QStringList sourceFileNameFilter() = 0;
+ virtual void parseHeaderFile(const Location& location,
+ const QString& filePath, Tree *tree);
+ virtual void parseSourceFile(const Location& location,
+ const QString& filePath, Tree *tree) = 0;
+ virtual void doneParsingHeaderFiles(Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree) = 0;
+
+ void createOutputSubdirectory(const Location& location, const QString& filePath);
+
+ static void initialize(const Config& config);
+ static void terminate();
+ static CodeParser *parserForLanguage(const QString& language);
+ static CodeParser *parserForHeaderFile(const QString &filePath);
+ static CodeParser *parserForSourceFile(const QString &filePath);
+ static const QString titleFromName(const QString& name);
+ static void setLink(Node* node, Node::LinkType linkType, const QString& arg);
+ static const QString& currentOutputSubdirectory() { return currentSubDir_; }
+
+protected:
+ QSet commonMetaCommands();
+ void processCommonMetaCommand(const Location& location,
+ const QString& command, const QString& arg,
+ Node *node, Tree *tree);
+ static void extractPageLinkAndDesc(const QString& arg,
+ QString* link,
+ QString* desc);
+
+private:
+ static QString currentSubDir_;
+ static QList parsers;
+ static bool showInternal;
+ static QMap nameToTitle;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp
new file mode 100644
index 0000000..f97b6ca
--- /dev/null
+++ b/src/tools/qdoc/config.cpp
@@ -0,0 +1,978 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.cpp
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "config.h"
+#include
+
+QT_BEGIN_NAMESPACE
+
+/*
+ An entry on the MetaStack.
+ */
+class MetaStackEntry
+{
+public:
+ void open();
+ void close();
+
+ QStringList accum;
+ QStringList next;
+};
+
+/*
+ */
+void MetaStackEntry::open()
+{
+ next.append(QString());
+}
+
+/*
+ */
+void MetaStackEntry::close()
+{
+ accum += next;
+ next.clear();
+}
+
+/*
+ ###
+*/
+class MetaStack : private QStack
+{
+public:
+ MetaStack();
+
+ void process(QChar ch, const Location& location);
+ QStringList getExpanded(const Location& location);
+};
+
+MetaStack::MetaStack()
+{
+ push(MetaStackEntry());
+ top().open();
+}
+
+void MetaStack::process(QChar ch, const Location& location)
+{
+ if (ch == QLatin1Char('{')) {
+ push(MetaStackEntry());
+ top().open();
+ }
+ else if (ch == QLatin1Char('}')) {
+ if (count() == 1)
+ location.fatal(tr("Unexpected '}'"));
+
+ top().close();
+ QStringList suffixes = pop().accum;
+ QStringList prefixes = top().next;
+
+ top().next.clear();
+ QStringList::ConstIterator pre = prefixes.begin();
+ while (pre != prefixes.end()) {
+ QStringList::ConstIterator suf = suffixes.begin();
+ while (suf != suffixes.end()) {
+ top().next << (*pre + *suf);
+ ++suf;
+ }
+ ++pre;
+ }
+ }
+ else if (ch == QLatin1Char(',') && count() > 1) {
+ top().close();
+ top().open();
+ }
+ else {
+ QStringList::Iterator pre = top().next.begin();
+ while (pre != top().next.end()) {
+ *pre += ch;
+ ++pre;
+ }
+ }
+}
+
+QStringList MetaStack::getExpanded(const Location& location)
+{
+ if (count() > 1)
+ location.fatal(tr("Missing '}'"));
+
+ top().close();
+ return top().accum;
+}
+
+QT_STATIC_CONST_IMPL QString Config::dot = QLatin1String(".");
+QString Config::overrideOutputDir;
+QSet Config::overrideOutputFormats;
+QMap Config::extractedDirs;
+int Config::numInstances;
+
+/*!
+ \class Config
+ \brief The Config class contains the configuration variables
+ for controlling how qdoc produces documentation.
+
+ Its load() function, reads, parses, and processes a qdocconf file.
+ */
+
+/*!
+ The constructor sets the \a programName and initializes all
+ internal state variables to empty values.
+ */
+Config::Config(const QString& programName)
+ : prog(programName)
+{
+ loc = Location::null;
+ lastLoc = Location::null;
+ locMap.clear();
+ stringValueMap.clear();
+ stringListValueMap.clear();
+ numInstances++;
+}
+
+/*!
+ The destructor has nothing special to do.
+ */
+Config::~Config()
+{
+}
+
+/*!
+ Loads and parses the qdoc configuration file \a fileName.
+ This function calls the other load() function, which does
+ the loading, parsing, and processing of the configuration
+ file.
+
+ Intializes the location variables returned by location()
+ and lastLocation().
+ */
+void Config::load(const QString& fileName)
+{
+ load(Location::null, fileName);
+ if (loc.isEmpty()) {
+ loc = Location(fileName);
+ }
+ else {
+ loc.setEtc(true);
+ }
+ lastLoc = Location::null;
+}
+
+/*!
+ Writes the qdoc configuration data to the named file.
+ The previous contents of the file are overwritten.
+ */
+void Config::unload(const QString& fileName)
+{
+ QStringMultiMap::ConstIterator v = stringValueMap.begin();
+ while (v != stringValueMap.end()) {
+ qDebug() << v.key() << " = " << v.value();
+#if 0
+ if (v.key().startsWith(varDot)) {
+ QString subVar = v.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ t.insert(subVar,v.value());
+ }
+#endif
+ ++v;
+ }
+ qDebug() << "fileName:" << fileName;
+}
+
+/*!
+ Joins all the strings in \a values into a single string with the
+ individual \a values separated by ' '. Then it inserts the result
+ into the string list map with \a var as the key.
+
+ It also inserts the \a values string list into a separate map,
+ also with \a var as the key.
+ */
+void Config::setStringList(const QString& var, const QStringList& values)
+{
+ stringValueMap[var] = values.join(QLatin1String(" "));
+ stringListValueMap[var] = values;
+}
+
+/*!
+ Looks up the configuarion variable \a var in the string
+ map and returns the boolean value.
+ */
+bool Config::getBool(const QString& var) const
+{
+ return QVariant(getString(var)).toBool();
+}
+
+/*!
+ Looks up the configuration variable \a var in the string list
+ map. Iterates through the string list found, interpreting each
+ string in the list as an integer and adding it to a total sum.
+ Returns the sum.
+ */
+int Config::getInt(const QString& var) const
+{
+ QStringList strs = getStringList(var);
+ QStringList::ConstIterator s = strs.begin();
+ int sum = 0;
+
+ while (s != strs.end()) {
+ sum += (*s).toInt();
+ ++s;
+ }
+ return sum;
+}
+
+/*!
+ Function to return the correct outputdir.
+ outputdir can be set using the qdocconf or the command-line
+ variable -outputdir.
+ */
+QString Config::getOutputDir() const
+{
+ if (overrideOutputDir.isNull())
+ return getString(QLatin1String(CONFIG_OUTPUTDIR));
+ else
+ return overrideOutputDir;
+}
+
+/*!
+ Function to return the correct outputformats.
+ outputformats can be set using the qdocconf or the command-line
+ variable -outputformat.
+ */
+QSet Config::getOutputFormats() const
+{
+ if (overrideOutputFormats.isEmpty())
+ return getStringSet(QLatin1String(CONFIG_OUTPUTFORMATS));
+ else
+ return overrideOutputFormats;
+}
+
+/*!
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLoc} to the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ map, and returns the string that \a var maps to.
+ */
+QString Config::getString(const QString& var) const
+{
+ if (!locMap[var].isEmpty())
+ (Location&) lastLoc = locMap[var];
+ return stringValueMap[var];
+}
+
+/*!
+ Looks up the configuration variable \a var in the string
+ list map, converts the string list it maps to into a set
+ of strings, and returns the set.
+ */
+QSet Config::getStringSet(const QString& var) const
+{
+ return QSet::fromList(getStringList(var));
+}
+
+/*!
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLoc} the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ list map, and returns the string list that \a var maps to.
+ */
+QStringList Config::getStringList(const QString& var) const
+{
+ if (!locMap[var].isEmpty())
+ (Location&) lastLoc = locMap[var];
+ return stringListValueMap[var];
+}
+
+/*!
+ This function should only be called when the configuration
+ variable \a var maps to a string list that contains file paths.
+ It cleans the paths with QDir::cleanPath() before returning
+ them.
+
+ First, this function looks up the configuration variable \a var
+ in the location map and, if found, sets the internal variable
+ \c{lastLoc} the Location that \a var maps to.
+
+ Then it looks up the configuration variable \a var in the string
+ list map, which maps to a string list that contains file paths.
+ These paths might not be clean, so QDir::cleanPath() is called
+ for each one. The string list returned contains cleaned paths.
+ */
+QStringList Config::getCleanPathList(const QString& var) const
+{
+ if (!locMap[var].isEmpty())
+ (Location&) lastLoc = locMap[var];
+ QStringList t;
+ QMap::const_iterator it = stringListValueMap.find(var);
+ if (it != stringListValueMap.end()) {
+ const QStringList& sl = it.value();
+ if (!sl.isEmpty()) {
+ t.reserve(sl.size());
+ for (int i=0; i subRegExps = getRegExpList(var);
+ QList::ConstIterator s = subRegExps.begin();
+
+ while (s != subRegExps.end()) {
+ if (!(*s).isValid())
+ return *s;
+ if (!pattern.isEmpty())
+ pattern += QLatin1Char('|');
+ pattern += QLatin1String("(?:") + (*s).pattern() + QLatin1Char(')');
+ ++s;
+ }
+ if (pattern.isEmpty())
+ pattern = QLatin1String("$x"); // cannot match
+ return QRegExp(pattern);
+}
+
+/*!
+ Looks up the configuration variable \a var in the string list
+ map, converts the string list to a list of regular expressions,
+ and returns it.
+ */
+QList Config::getRegExpList(const QString& var) const
+{
+ QStringList strs = getStringList(var);
+ QStringList::ConstIterator s = strs.begin();
+ QList regExps;
+
+ while (s != strs.end()) {
+ regExps += QRegExp(*s);
+ ++s;
+ }
+ return regExps;
+}
+
+/*!
+ This function is slower than it could be. What it does is
+ find all the keys that begin with \a var + dot and return
+ the matching keys in a set, stripped of the matching prefix
+ and dot.
+ */
+QSet Config::subVars(const QString& var) const
+{
+ QSet result;
+ QString varDot = var + QLatin1Char('.');
+ QStringMultiMap::ConstIterator v = stringValueMap.begin();
+ while (v != stringValueMap.end()) {
+ if (v.key().startsWith(varDot)) {
+ QString subVar = v.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ result.insert(subVar);
+ }
+ ++v;
+ }
+ return result;
+}
+
+/*!
+ Same as subVars(), but in this case we return a string map
+ with the matching keys (stripped of the prefix \a var and
+ mapped to their values. The pairs are inserted into \a t
+ */
+void Config::subVarsAndValues(const QString& var, QStringMultiMap& t) const
+{
+ QString varDot = var + QLatin1Char('.');
+ QStringMultiMap::ConstIterator v = stringValueMap.begin();
+ while (v != stringValueMap.end()) {
+ if (v.key().startsWith(varDot)) {
+ QString subVar = v.key().mid(varDot.length());
+ int dot = subVar.indexOf(QLatin1Char('.'));
+ if (dot != -1)
+ subVar.truncate(dot);
+ t.insert(subVar,v.value());
+ }
+ ++v;
+ }
+}
+
+/*!
+ Builds and returns a list of file pathnames for the file
+ type specified by \a filesVar (e.g. "headers" or "sources").
+ The files are found in the directories specified by
+ \a dirsVar, and they are filtered by \a defaultNameFilter
+ if a better filter can't be constructed from \a filesVar.
+ The directories in \a excludedDirs are avoided. The files
+ in \a excludedFiles are not included in the return list.
+ */
+QStringList Config::getAllFiles(const QString &filesVar,
+ const QString &dirsVar,
+ const QSet &excludedDirs,
+ const QSet &excludedFiles)
+{
+ QStringList result = getStringList(filesVar);
+ QStringList dirs = getStringList(dirsVar);
+
+ QString nameFilter = getString(filesVar + dot + QLatin1String(CONFIG_FILEEXTENSIONS));
+
+ QStringList::ConstIterator d = dirs.begin();
+ while (d != dirs.end()) {
+ result += getFilesHere(*d, nameFilter, excludedDirs, excludedFiles);
+ ++d;
+ }
+ return result;
+}
+
+/*!
+ \a fileName is the path of the file to find.
+
+ \a files and \a dirs are the lists where we must find the
+ components of \a fileName.
+
+ \a location is used for obtaining the file and line numbers
+ for report qdoc errors.
+ */
+QString Config::findFile(const Location& location,
+ const QStringList& files,
+ const QStringList& dirs,
+ const QString& fileName,
+ QString& userFriendlyFilePath)
+{
+ if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) {
+ userFriendlyFilePath = fileName;
+ return fileName;
+ }
+
+ QFileInfo fileInfo;
+ QStringList components = fileName.split(QLatin1Char('?'));
+ QString firstComponent = components.first();
+
+ QStringList::ConstIterator f = files.begin();
+ while (f != files.end()) {
+ if (*f == firstComponent ||
+ (*f).endsWith(QLatin1Char('/') + firstComponent)) {
+ fileInfo.setFile(*f);
+ if (!fileInfo.exists())
+ location.fatal(tr("File '%1' does not exist").arg(*f));
+ break;
+ }
+ ++f;
+ }
+
+ if (fileInfo.fileName().isEmpty()) {
+ QStringList::ConstIterator d = dirs.begin();
+ while (d != dirs.end()) {
+ fileInfo.setFile(QDir(*d), firstComponent);
+ if (fileInfo.exists()) {
+ break;
+ }
+ ++d;
+ }
+ }
+
+ userFriendlyFilePath = QString();
+ if (!fileInfo.exists())
+ return QString();
+
+ QStringList::ConstIterator c = components.begin();
+ for (;;) {
+ bool isArchive = (c != components.end() - 1);
+ QString userFriendly = *c;
+
+ userFriendlyFilePath += userFriendly;
+
+ if (isArchive) {
+ QString extracted = extractedDirs[fileInfo.filePath()];
+ ++c;
+ fileInfo.setFile(QDir(extracted), *c);
+ }
+ else
+ break;
+
+ userFriendlyFilePath += QLatin1Char('?');
+ }
+ return fileInfo.filePath();
+}
+
+/*!
+ */
+QString Config::findFile(const Location& location,
+ const QStringList& files,
+ const QStringList& dirs,
+ const QString& fileBase,
+ const QStringList& fileExtensions,
+ QString& userFriendlyFilePath)
+{
+ QStringList::ConstIterator e = fileExtensions.begin();
+ while (e != fileExtensions.end()) {
+ QString filePath = findFile(location,
+ files,
+ dirs,
+ fileBase + QLatin1Char('.') + *e,
+ userFriendlyFilePath);
+ if (!filePath.isEmpty())
+ return filePath;
+ ++e;
+ }
+ return findFile(location, files, dirs, fileBase, userFriendlyFilePath);
+}
+
+/*!
+ Copies the \a sourceFilePath to the file name constructed by
+ concatenating \a targetDirPath and \a userFriendlySourceFilePath.
+ \a location is for identifying the file and line number where
+ a qdoc error occurred. The constructed output file name is
+ returned.
+ */
+QString Config::copyFile(const Location& location,
+ const QString& sourceFilePath,
+ const QString& userFriendlySourceFilePath,
+ const QString& targetDirPath)
+{
+ QFile inFile(sourceFilePath);
+ if (!inFile.open(QFile::ReadOnly)) {
+ location.fatal(tr("Cannot open input file '%1': %2")
+ .arg(sourceFilePath).arg(inFile.errorString()));
+ return QString();
+ }
+
+ QString outFileName = userFriendlySourceFilePath;
+ int slash = outFileName.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1)
+ outFileName = outFileName.mid(slash);
+
+ QFile outFile(targetDirPath + QLatin1Char('/') + outFileName);
+ if (!outFile.open(QFile::WriteOnly)) {
+ location.fatal(tr("Cannot open output file '%1': %2")
+ .arg(outFile.fileName()).arg(outFile.errorString()));
+ return QString();
+ }
+
+ char buffer[1024];
+ int len;
+ while ((len = inFile.read(buffer, sizeof(buffer))) > 0) {
+ outFile.write(buffer, len);
+ }
+ return outFileName;
+}
+
+/*!
+ Finds the largest unicode digit in \a value in the range
+ 1..7 and returns it.
+ */
+int Config::numParams(const QString& value)
+{
+ int max = 0;
+ for (int i = 0; i != value.length(); i++) {
+ uint c = value[i].unicode();
+ if (c > 0 && c < 8)
+ max = qMax(max, (int)c);
+ }
+ return max;
+}
+
+/*!
+ Removes everything from \a dir. This function is recursive.
+ It doesn't remove \a dir itself, but if it was called
+ recursively, then the caller will remove \a dir.
+ */
+bool Config::removeDirContents(const QString& dir)
+{
+ QDir dirInfo(dir);
+ QFileInfoList entries = dirInfo.entryInfoList();
+
+ bool ok = true;
+
+ QFileInfoList::Iterator it = entries.begin();
+ while (it != entries.end()) {
+ if ((*it).isFile()) {
+ if (!dirInfo.remove((*it).fileName()))
+ ok = false;
+ }
+ else if ((*it).isDir()) {
+ if ((*it).fileName() != QLatin1String(".") && (*it).fileName() != QLatin1String("..")) {
+ if (removeDirContents((*it).absoluteFilePath())) {
+ if (!dirInfo.rmdir((*it).fileName()))
+ ok = false;
+ }
+ else {
+ ok = false;
+ }
+ }
+ }
+ ++it;
+ }
+ return ok;
+}
+
+/*!
+ Returns true if \a ch is a letter, number, '_', '.',
+ '{', '}', or ','.
+ */
+bool Config::isMetaKeyChar(QChar ch)
+{
+ return ch.isLetterOrNumber()
+ || ch == QLatin1Char('_')
+ || ch == QLatin1Char('.')
+ || ch == QLatin1Char('{')
+ || ch == QLatin1Char('}')
+ || ch == QLatin1Char(',');
+}
+
+/*!
+ Load, parse, and process a qdoc configuration file. This
+ function is only called by the other load() function, but
+ this one is recursive, i.e., it calls itself when it sees
+ an \c{include} statement in the qdoc configuration file.
+ */
+void Config::load(Location location, const QString& fileName)
+{
+ QRegExp keySyntax(QLatin1String("\\w+(?:\\.\\w+)*"));
+
+#define SKIP_CHAR() \
+ do { \
+ location.advance(c); \
+ ++i; \
+ c = text.at(i); \
+ cc = c.unicode(); \
+} while (0)
+
+#define SKIP_SPACES() \
+ while (c.isSpace() && cc != '\n') \
+ SKIP_CHAR()
+
+#define PUT_CHAR() \
+ word += c; \
+ SKIP_CHAR();
+
+ if (location.depth() > 16)
+ location.fatal(tr("Too many nested includes"));
+
+ QFile fin(fileName);
+ if (!fin.open(QFile::ReadOnly | QFile::Text)) {
+ fin.setFileName(fileName + QLatin1String(".qdoc"));
+ if (!fin.open(QFile::ReadOnly | QFile::Text))
+ location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
+ }
+
+ QTextStream stream(&fin);
+ stream.setCodec("UTF-8");
+ QString text = stream.readAll();
+ text += QLatin1String("\n\n");
+ text += QLatin1Char('\0');
+ fin.close();
+
+ location.push(fileName);
+ location.start();
+
+ int i = 0;
+ QChar c = text.at(0);
+ uint cc = c.unicode();
+ while (i < (int) text.length()) {
+ if (cc == 0)
+ ++i;
+ else if (c.isSpace()) {
+ SKIP_CHAR();
+ }
+ else if (cc == '#') {
+ do {
+ SKIP_CHAR();
+ } while (cc != '\n');
+ }
+ else if (isMetaKeyChar(c)) {
+ Location keyLoc = location;
+ bool plus = false;
+ QString stringValue;
+ QStringList stringListValue;
+ QString word;
+ bool inQuote = false;
+ bool prevWordQuoted = true;
+ bool metWord = false;
+
+ MetaStack stack;
+ do {
+ stack.process(c, location);
+ SKIP_CHAR();
+ } while (isMetaKeyChar(c));
+
+ QStringList keys = stack.getExpanded(location);
+ SKIP_SPACES();
+
+ if (keys.count() == 1 && keys.first() == QLatin1String("include")) {
+ QString includeFile;
+
+ if (cc != '(')
+ location.fatal(tr("Bad include syntax"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+
+ while (!c.isSpace() && cc != '#' && cc != ')') {
+
+ if (cc == '$') {
+ QString var;
+ SKIP_CHAR();
+ while (c.isLetterOrNumber() || cc == '_') {
+ var += c;
+ SKIP_CHAR();
+ }
+ if (!var.isEmpty()) {
+ char *val = getenv(var.toLatin1().data());
+ if (val == 0) {
+ location.fatal(tr("Environment variable '%1' undefined").arg(var));
+ }
+ else {
+ includeFile += QString::fromLatin1(val);
+ }
+ }
+ } else {
+ includeFile += c;
+ SKIP_CHAR();
+ }
+ }
+ SKIP_SPACES();
+ if (cc != ')')
+ location.fatal(tr("Bad include syntax"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+ if (cc != '#' && cc != '\n')
+ location.fatal(tr("Trailing garbage"));
+
+ /*
+ Here is the recursive call.
+ */
+ load(location,
+ QFileInfo(QFileInfo(fileName).dir(), includeFile)
+ .filePath());
+ }
+ else {
+ /*
+ It wasn't an include statement, so it's something else.
+ */
+ if (cc == '+') {
+ plus = true;
+ SKIP_CHAR();
+ }
+ if (cc != '=')
+ location.fatal(tr("Expected '=' or '+=' after key"));
+ SKIP_CHAR();
+ SKIP_SPACES();
+
+ for (;;) {
+ if (cc == '\\') {
+ int metaCharPos;
+
+ SKIP_CHAR();
+ if (cc == '\n') {
+ SKIP_CHAR();
+ }
+ else if (cc > '0' && cc < '8') {
+ word += QChar(c.digitValue());
+ SKIP_CHAR();
+ }
+ else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c)) != -1) {
+ word += QLatin1Char("\a\b\f\n\r\t\v"[metaCharPos]);
+ SKIP_CHAR();
+ }
+ else {
+ PUT_CHAR();
+ }
+ }
+ else if (c.isSpace() || cc == '#') {
+ if (inQuote) {
+ if (cc == '\n')
+ location.fatal(tr("Unterminated string"));
+ PUT_CHAR();
+ }
+ else {
+ if (!word.isEmpty()) {
+ if (metWord)
+ stringValue += QLatin1Char(' ');
+ stringValue += word;
+ stringListValue << word;
+ metWord = true;
+ word.clear();
+ prevWordQuoted = false;
+ }
+ if (cc == '\n' || cc == '#')
+ break;
+ SKIP_SPACES();
+ }
+ }
+ else if (cc == '"') {
+ if (inQuote) {
+ if (!prevWordQuoted)
+ stringValue += QLatin1Char(' ');
+ stringValue += word;
+ if (!word.isEmpty())
+ stringListValue << word;
+ metWord = true;
+ word.clear();
+ prevWordQuoted = true;
+ }
+ inQuote = !inQuote;
+ SKIP_CHAR();
+ }
+ else if (cc == '$') {
+ QString var;
+ SKIP_CHAR();
+ while (c.isLetterOrNumber() || cc == '_') {
+ var += c;
+ SKIP_CHAR();
+ }
+ if (!var.isEmpty()) {
+ char *val = getenv(var.toLatin1().data());
+ if (val == 0) {
+ location.fatal(tr("Environment variable '%1' undefined").arg(var));
+ }
+ else {
+ word += QString::fromLatin1(val);
+ }
+ }
+ }
+ else {
+ if (!inQuote && cc == '=')
+ location.fatal(tr("Unexpected '='"));
+ PUT_CHAR();
+ }
+ }
+
+ QStringList::ConstIterator key = keys.begin();
+ while (key != keys.end()) {
+ if (!keySyntax.exactMatch(*key))
+ keyLoc.fatal(tr("Invalid key '%1'").arg(*key));
+
+ if (plus) {
+ if (locMap[*key].isEmpty()) {
+ locMap[*key] = keyLoc;
+ }
+ else {
+ locMap[*key].setEtc(true);
+ }
+ if (stringValueMap[*key].isEmpty()) {
+ stringValueMap[*key] = stringValue;
+ }
+ else {
+ stringValueMap[*key] +=
+ QLatin1Char(' ') + stringValue;
+ }
+ stringListValueMap[*key] += stringListValue;
+ }
+ else {
+ locMap[*key] = keyLoc;
+ stringValueMap[*key] = stringValue;
+ stringListValueMap[*key] = stringListValue;
+ }
+ ++key;
+ }
+ }
+ }
+ else {
+ location.fatal(tr("Unexpected character '%1' at beginning of line")
+ .arg(c));
+ }
+ }
+}
+
+QStringList Config::getFilesHere(const QString& dir,
+ const QString& nameFilter,
+ const QSet &excludedDirs,
+ const QSet &excludedFiles)
+{
+ QStringList result;
+ if (excludedDirs.contains(dir))
+ return result;
+
+ QDir dirInfo(dir);
+ QStringList fileNames;
+ QStringList::const_iterator fn;
+
+ dirInfo.setNameFilters(nameFilter.split(QLatin1Char(' ')));
+ dirInfo.setSorting(QDir::Name);
+ dirInfo.setFilter(QDir::Files);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ if (!fn->startsWith(QLatin1Char('~'))) {
+ QString s = dirInfo.filePath(*fn);
+ QString c = QDir::cleanPath(s);
+ if (!excludedFiles.contains(c)) {
+ result.append(c);
+ }
+ }
+ ++fn;
+ }
+
+ dirInfo.setNameFilters(QStringList(QLatin1String("*")));
+ dirInfo.setFilter(QDir::Dirs|QDir::NoDotAndDotDot);
+ fileNames = dirInfo.entryList();
+ fn = fileNames.constBegin();
+ while (fn != fileNames.constEnd()) {
+ result += getFilesHere(dirInfo.filePath(*fn), nameFilter, excludedDirs, excludedFiles);
+ ++fn;
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h
new file mode 100644
index 0000000..461e0b9
--- /dev/null
+++ b/src/tools/qdoc/config.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ config.h
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include
+#include
+#include
+
+#include "location.h"
+
+QT_BEGIN_NAMESPACE
+
+typedef QMultiMap QStringMultiMap;
+
+class Config
+{
+public:
+ Config(const QString& programName);
+ ~Config();
+
+ void load(const QString& fileName);
+ void unload(const QString& fileName);
+ void setStringList(const QString& var, const QStringList& values);
+
+ const QString& programName() const { return prog; }
+ const Location& location() const { return loc; }
+ const Location& lastLocation() const { return lastLoc; }
+ bool getBool(const QString& var) const;
+ int getInt(const QString& var) const;
+ QString getOutputDir() const;
+ QSet getOutputFormats() const;
+ QString getString(const QString& var) const;
+ QSet getStringSet(const QString& var) const;
+ QStringList getStringList(const QString& var) const;
+ QStringList getCleanPathList(const QString& var) const;
+ QRegExp getRegExp(const QString& var) const;
+ QList getRegExpList(const QString& var) const;
+ QSet subVars(const QString& var) const;
+ void subVarsAndValues(const QString& var, QStringMultiMap& t) const;
+ QStringList getAllFiles(const QString& filesVar,
+ const QString& dirsVar,
+ const QSet &excludedDirs = QSet(),
+ const QSet &excludedFiles = QSet());
+
+ static QStringList getFilesHere(const QString& dir,
+ const QString& nameFilter,
+ const QSet &excludedDirs = QSet(),
+ const QSet &excludedFiles = QSet());
+ static QString findFile(const Location& location,
+ const QStringList &files,
+ const QStringList& dirs,
+ const QString& fileName,
+ QString& userFriendlyFilePath);
+ static QString findFile(const Location &location,
+ const QStringList &files,
+ const QStringList &dirs,
+ const QString &fileBase,
+ const QStringList &fileExtensions,
+ QString &userFriendlyFilePath);
+ static QString copyFile(const Location& location,
+ const QString& sourceFilePath,
+ const QString& userFriendlySourceFilePath,
+ const QString& targetDirPath);
+ static int numParams(const QString& value);
+ static bool removeDirContents(const QString& dir);
+
+ QT_STATIC_CONST QString dot;
+
+ static QString overrideOutputDir;
+ static QSet overrideOutputFormats;
+
+private:
+ static bool isMetaKeyChar(QChar ch);
+ void load(Location location, const QString& fileName);
+
+ QString prog;
+ Location loc;
+ Location lastLoc;
+ QMap locMap;
+ QMap stringListValueMap;
+ QMap stringValueMap;
+
+ static QMap uncompressedFiles;
+ static QMap extractedDirs;
+ static int numInstances;
+};
+
+#define CONFIG_ALIAS "alias"
+#define CONFIG_BASE "base" // ### don't document for now
+#define CONFIG_BASEDIR "basedir"
+#define CONFIG_CODEINDENT "codeindent"
+#define CONFIG_DEFINES "defines"
+#define CONFIG_DESCRIPTION "description"
+#define CONFIG_EDITION "edition"
+#define CONFIG_ENDHEADER "endheader"
+#define CONFIG_EXAMPLEDIRS "exampledirs"
+#define CONFIG_EXAMPLES "examples"
+#define CONFIG_EXCLUDEDIRS "excludedirs"
+#define CONFIG_EXCLUDEFILES "excludefiles"
+#define CONFIG_EXTRAIMAGES "extraimages"
+#define CONFIG_FALSEHOODS "falsehoods"
+#define CONFIG_FORMATTING "formatting"
+#define CONFIG_GENERATEINDEX "generateindex"
+#define CONFIG_HEADERDIRS "headerdirs"
+#define CONFIG_HEADERS "headers"
+#define CONFIG_HEADERSCRIPTS "headerscripts"
+#define CONFIG_HEADERSTYLES "headerstyles"
+#define CONFIG_IGNOREDIRECTIVES "ignoredirectives"
+#define CONFIG_IGNORETOKENS "ignoretokens"
+#define CONFIG_IMAGEDIRS "imagedirs"
+#define CONFIG_IMAGES "images"
+#define CONFIG_INDEXES "indexes"
+#define CONFIG_LANGUAGE "language"
+#define CONFIG_MACRO "macro"
+#define CONFIG_NATURALLANGUAGE "naturallanguage"
+#define CONFIG_OBSOLETELINKS "obsoletelinks"
+#define CONFIG_OUTPUTDIR "outputdir"
+#define CONFIG_OUTPUTENCODING "outputencoding"
+#define CONFIG_OUTPUTLANGUAGE "outputlanguage"
+#define CONFIG_OUTPUTFORMATS "outputformats"
+#define CONFIG_OUTPUTPREFIXES "outputprefixes"
+#define CONFIG_PROJECT "project"
+#define CONFIG_QHP "qhp"
+#define CONFIG_QUOTINGINFORMATION "quotinginformation"
+#define CONFIG_SCRIPTDIRS "scriptdirs"
+#define CONFIG_SCRIPTS "scripts"
+#define CONFIG_SHOWINTERNAL "showinternal"
+#define CONFIG_SOURCEDIRS "sourcedirs"
+#define CONFIG_SOURCEENCODING "sourceencoding"
+#define CONFIG_SOURCEMODULES "sourcemodules"
+#define CONFIG_SOURCES "sources"
+#define CONFIG_SPURIOUS "spurious"
+#define CONFIG_STYLEDIRS "styledirs"
+#define CONFIG_STYLE "style"
+#define CONFIG_STYLES "styles"
+#define CONFIG_STYLESHEETS "stylesheets"
+#define CONFIG_SYNTAXHIGHLIGHTING "syntaxhighlighting"
+#define CONFIG_TEMPLATEDIR "templatedir"
+#define CONFIG_TABSIZE "tabsize"
+#define CONFIG_TAGFILE "tagfile"
+#define CONFIG_TRANSLATORS "translators" // ### don't document for now
+#define CONFIG_URL "url"
+#define CONFIG_VERSION "version"
+#define CONFIG_VERSIONSYM "versionsym"
+
+#define CONFIG_FILEEXTENSIONS "fileextensions"
+#define CONFIG_IMAGEEXTENSIONS "imageextensions"
+
+#ifdef QDOC_QML
+#define CONFIG_QMLONLY "qmlonly"
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/cppcodemarker.cpp b/src/tools/qdoc/cppcodemarker.cpp
new file mode 100644
index 0000000..cc68865
--- /dev/null
+++ b/src/tools/qdoc/cppcodemarker.cpp
@@ -0,0 +1,1354 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.cpp
+*/
+
+#include "atom.h"
+#include "cppcodemarker.h"
+#include "node.h"
+#include "text.h"
+#include "tree.h"
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ The constructor does nothing.
+ */
+CppCodeMarker::CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ The destructor does nothing.
+ */
+CppCodeMarker::~CppCodeMarker()
+{
+ // nothing.
+}
+
+/*!
+ Returns true.
+ */
+bool CppCodeMarker::recognizeCode(const QString & /* code */)
+{
+ return true;
+}
+
+/*!
+ Returns true if \a ext is any of a list of file extensions
+ for the C++ language.
+ */
+bool CppCodeMarker::recognizeExtension(const QString& extension)
+{
+ QByteArray ext = extension.toLatin1();
+ return ext == "c" ||
+ ext == "c++" ||
+ ext == "cc" ||
+ ext == "cpp" ||
+ ext == "cxx" ||
+ ext == "ch" ||
+ ext == "h" ||
+ ext == "h++" ||
+ ext == "hh" ||
+ ext == "hpp" ||
+ ext == "hxx";
+}
+
+/*!
+ Returns true if \a lang is either "C" or "Cpp".
+ */
+bool CppCodeMarker::recognizeLanguage(const QString &lang)
+{
+ return lang == QLatin1String("C") || lang == QLatin1String("Cpp");
+}
+
+/*!
+ Returns the type of atom used to represent C++ code in the documentation.
+*/
+Atom::Type CppCodeMarker::atomType() const
+{
+ return Atom::Code;
+}
+
+/*!
+ Returns the \a node name, or "()" if \a node is a
+ Node::Function node.
+ */
+QString CppCodeMarker::plainName(const Node *node)
+{
+ QString name = node->name();
+ if (node->type() == Node::Function)
+ name += QLatin1String("()");
+ return name;
+}
+
+QString CppCodeMarker::plainFullName(const Node *node, const Node *relative)
+{
+ if (node->name().isEmpty()) {
+ return QLatin1String("global");
+ }
+ else {
+ QString fullName;
+ while (node) {
+ fullName.prepend(plainName(node));
+ if (node->parent() == relative ||
+ node->parent()->subType() == Node::Collision ||
+ node->parent()->name().isEmpty())
+ break;
+ fullName.prepend(QLatin1String("::"));
+ node = node->parent();
+ }
+ return fullName;
+ }
+}
+
+QString CppCodeMarker::markedUpCode(const QString &code,
+ const Node *relative,
+ const Location &location)
+{
+ return addMarkUp(code, relative, location);
+}
+
+QString CppCodeMarker::markedUpSynopsis(const Node *node,
+ const Node * /* relative */,
+ SynopsisStyle style)
+{
+ const int MaxEnumValues = 6;
+ const FunctionNode *func;
+ const PropertyNode *property;
+ const VariableNode *variable;
+ const EnumNode *enume;
+ const TypedefNode *typedeff;
+ QString synopsis;
+ QString extra;
+ QString name;
+
+ name = taggedNode(node);
+ if (style != Detailed)
+ name = linkTag(node, name);
+ name = "<@name>" + name + "@name>";
+
+ if (style == Detailed && !node->parent()->name().isEmpty() &&
+ node->type() != Node::Property)
+ name.prepend(taggedNode(node->parent()) + "::");
+
+ switch (node->type()) {
+ case Node::Namespace:
+ synopsis = "namespace " + name;
+ break;
+ case Node::Class:
+ synopsis = "class " + name;
+ break;
+ case Node::Function:
+ case Node::QmlSignal:
+ case Node::QmlSignalHandler:
+ case Node::QmlMethod:
+ func = (const FunctionNode *) node;
+ if (style != Subpage && !func->returnType().isEmpty())
+ synopsis = typified(func->returnType()) + QLatin1Char(' ');
+ synopsis += name;
+ if (func->metaness() != FunctionNode::MacroWithoutParams) {
+ synopsis += "(";
+ if (!func->parameters().isEmpty()) {
+ //synopsis += QLatin1Char(' ');
+ QList::ConstIterator p = func->parameters().begin();
+ while (p != func->parameters().end()) {
+ if (p != func->parameters().begin())
+ synopsis += ", ";
+ synopsis += typified((*p).leftType());
+ if (style != Subpage && !(*p).name().isEmpty())
+ synopsis +=
+ "<@param>" + protect((*p).name()) + "@param>";
+ synopsis += protect((*p).rightType());
+ if (style != Subpage && !(*p).defaultValue().isEmpty())
+ synopsis += " = " + protect((*p).defaultValue());
+ ++p;
+ }
+ //synopsis += QLatin1Char(' ');
+ }
+ synopsis += QLatin1Char(')');
+ }
+ if (func->isConst())
+ synopsis += " const";
+
+ if (style == Summary || style == Accessors) {
+ if (func->virtualness() != FunctionNode::NonVirtual)
+ synopsis.prepend("virtual ");
+ if (func->virtualness() == FunctionNode::PureVirtual)
+ synopsis.append(" = 0");
+ }
+ else if (style == Subpage) {
+ if (!func->returnType().isEmpty() && func->returnType() != "void")
+ synopsis += " : " + typified(func->returnType());
+ }
+ else {
+ QStringList bracketed;
+ if (func->isStatic()) {
+ bracketed += "static";
+ }
+ else if (func->virtualness() != FunctionNode::NonVirtual) {
+ if (func->virtualness() == FunctionNode::PureVirtual)
+ bracketed += "pure";
+ bracketed += "virtual";
+ }
+
+ if (func->access() == Node::Protected) {
+ bracketed += "protected";
+ }
+ else if (func->access() == Node::Private) {
+ bracketed += "private";
+ }
+
+ if (func->metaness() == FunctionNode::Signal) {
+ bracketed += "signal";
+ }
+ else if (func->metaness() == FunctionNode::Slot) {
+ bracketed += "slot";
+ }
+ if (!bracketed.isEmpty())
+ extra += " [" + bracketed.join(" ") + QLatin1Char(']');
+ }
+ break;
+ case Node::Enum:
+ enume = static_cast(node);
+ synopsis = "enum " + name;
+ if (style == Summary) {
+ synopsis += " { ";
+
+ QStringList documentedItems = enume->doc().enumItemNames();
+ if (documentedItems.isEmpty()) {
+ foreach (const EnumItem &item, enume->items())
+ documentedItems << item.name();
+ }
+ QStringList omitItems = enume->doc().omitEnumItemNames();
+ foreach (const QString &item, omitItems)
+ documentedItems.removeAll(item);
+
+ if (documentedItems.size() <= MaxEnumValues) {
+ for (int i = 0; i < documentedItems.size(); ++i) {
+ if (i != 0)
+ synopsis += ", ";
+ synopsis += documentedItems.at(i);
+ }
+ }
+ else {
+ for (int i = 0; i < documentedItems.size(); ++i) {
+ if (i < MaxEnumValues-2 || i == documentedItems.size()-1) {
+ if (i != 0)
+ synopsis += ", ";
+ synopsis += documentedItems.at(i);
+ }
+ else if (i == MaxEnumValues - 1) {
+ synopsis += ", ...";
+ }
+ }
+ }
+ if (!documentedItems.isEmpty())
+ synopsis += QLatin1Char(' ');
+ synopsis += QLatin1Char('}');
+ }
+ break;
+ case Node::Typedef:
+ typedeff = static_cast(node);
+ if (typedeff->associatedEnum()) {
+ synopsis = "flags " + name;
+ }
+ else {
+ synopsis = "typedef " + name;
+ }
+ break;
+ case Node::Property:
+ property = static_cast(node);
+ synopsis = name + " : " + typified(property->qualifiedDataType());
+ break;
+ case Node::Variable:
+ variable = static_cast(node);
+ if (style == Subpage) {
+ synopsis = name + " : " + typified(variable->dataType());
+ }
+ else {
+ synopsis = typified(variable->leftType()) + QLatin1Char(' ') +
+ name + protect(variable->rightType());
+ }
+ break;
+ default:
+ synopsis = name;
+ }
+
+ if (style == Summary) {
+ if (node->status() == Node::Preliminary) {
+ extra += " (preliminary)";
+ }
+ else if (node->status() == Node::Deprecated) {
+ extra += " (deprecated)";
+ }
+ else if (node->status() == Node::Obsolete) {
+ extra += " (obsolete)";
+ }
+ }
+
+ if (!extra.isEmpty()) {
+ extra.prepend("<@extra>");
+ extra.append("@extra>");
+ }
+ return synopsis + extra;
+}
+
+/*!
+ */
+QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary)
+{
+ QString name = taggedQmlNode(node);
+ if (summary) {
+ name = linkTag(node,name);
+ } else if (node->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(node);
+ if (pn->isAttached())
+ name.prepend(pn->element() + QLatin1Char('.'));
+ }
+ name = "<@name>" + name + "@name>";
+ QString synopsis = name;
+ if (node->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(node);
+ synopsis += " : " + typified(pn->dataType());
+ }
+
+ QString extra;
+ if (summary) {
+ if (node->status() == Node::Preliminary) {
+ extra += " (preliminary)";
+ }
+ else if (node->status() == Node::Deprecated) {
+ extra += " (deprecated)";
+ }
+ else if (node->status() == Node::Obsolete) {
+ extra += " (obsolete)";
+ }
+ }
+
+ if (!extra.isEmpty()) {
+ extra.prepend("<@extra>");
+ extra.append("@extra>");
+ }
+ return synopsis + extra;
+}
+
+QString CppCodeMarker::markedUpName(const Node *node)
+{
+ QString name = linkTag(node, taggedNode(node));
+ if (node->type() == Node::Function)
+ name += "()";
+ return name;
+}
+
+QString CppCodeMarker::markedUpFullName(const Node *node, const Node *relative)
+{
+ if (node->name().isEmpty()) {
+ return "global";
+ }
+ else {
+ QString fullName;
+ for (;;) {
+ fullName.prepend(markedUpName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend("<@op>::@op>");
+ node = node->parent();
+ }
+ return fullName;
+ }
+}
+
+QString CppCodeMarker::markedUpEnumValue(const QString &enumValue,
+ const Node *relative)
+{
+ const Node *node = relative->parent();
+ QString fullName;
+ while (node->parent()) {
+ fullName.prepend(markedUpName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ fullName.prepend("<@op>::@op>");
+ node = node->parent();
+ }
+ if (!fullName.isEmpty())
+ fullName.append("<@op>::@op>");
+ fullName.append(enumValue);
+ return fullName;
+}
+
+QString CppCodeMarker::markedUpIncludes(const QStringList& includes)
+{
+ QString code;
+
+ QStringList::ConstIterator inc = includes.begin();
+ while (inc != includes.end()) {
+ code += "<@preprocessor>#include <<@headerfile>" + *inc + "@headerfile>>@preprocessor>\n";
+ ++inc;
+ }
+ return code;
+}
+
+QString CppCodeMarker::functionBeginRegExp(const QString& funcName)
+{
+ return QLatin1Char('^') + QRegExp::escape(funcName) + QLatin1Char('$');
+
+}
+
+QString CppCodeMarker::functionEndRegExp(const QString& /* funcName */)
+{
+ return "^\\}$";
+}
+
+QList CppCodeMarker::sections(const InnerNode *inner,
+ SynopsisStyle style,
+ Status status)
+{
+ QList sections;
+
+ if (inner->type() == Node::Class) {
+ const ClassNode *classe = static_cast(inner);
+
+ if (style == Summary) {
+ FastSection privateFunctions(classe,
+ "Private Functions",
+ "",
+ "private function",
+ "private functions");
+ FastSection privateSlots(classe, "Private Slots", "", "private slot", "private slots");
+ FastSection privateTypes(classe, "Private Types", "", "private type", "private types");
+ FastSection protectedFunctions(classe,
+ "Protected Functions",
+ "",
+ "protected function",
+ "protected functions");
+ FastSection protectedSlots(classe,
+ "Protected Slots",
+ "",
+ "protected slot",
+ "protected slots");
+ FastSection protectedTypes(classe,
+ "Protected Types",
+ "",
+ "protected type",
+ "protected types");
+ FastSection protectedVariables(classe,
+ "Protected Variables",
+ "",
+ "protected type",
+ "protected variables");
+ FastSection publicFunctions(classe,
+ "Public Functions",
+ "",
+ "public function",
+ "public functions");
+ FastSection publicSignals(classe, "Signals", "", "signal", "signal");
+ FastSection publicSlots(classe, "Public Slots", "", "public slot", "public slots");
+ FastSection publicTypes(classe, "Public Types", "", "public type", "public types");
+ FastSection publicVariables(classe,
+ "Public Variables",
+ "",
+ "public variable",
+ "public variables");
+ FastSection properties(classe, "Properties", "", "property", "properties");
+ FastSection relatedNonMembers(classe,
+ "Related Non-Members",
+ "",
+ "related non-member",
+ "related non-members");
+ FastSection staticPrivateMembers(classe,
+ "Static Private Members",
+ "",
+ "static private member",
+ "static private members");
+ FastSection staticProtectedMembers(classe,
+ "Static Protected Members",
+ "",
+ "static protected member",
+ "static protected members");
+ FastSection staticPublicMembers(classe,
+ "Static Public Members",
+ "",
+ "static public member",
+ "static public members");
+ FastSection macros(inner, "Macros", "", "macro", "macros");
+
+ NodeList::ConstIterator r = classe->relatedNodes().begin();
+ while (r != classe->relatedNodes().end()) {
+ if ((*r)->type() == Node::Function) {
+ FunctionNode *func = static_cast(*r);
+ if (func->isMacro())
+ insert(macros, *r, style, status);
+ else
+ insert(relatedNonMembers, *r, style, status);
+ }
+ else {
+ insert(relatedNonMembers, *r, style, status);
+ }
+ ++r;
+ }
+
+ QStack stack;
+ stack.push(classe);
+
+ while (!stack.isEmpty()) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while (c != ancestorClass->childNodes().end()) {
+ bool isSlot = false;
+ bool isSignal = false;
+ bool isStatic = false;
+ if ((*c)->type() == Node::Function) {
+ const FunctionNode *func = (const FunctionNode *) *c;
+ isSlot = (func->metaness() == FunctionNode::Slot);
+ isSignal = (func->metaness() == FunctionNode::Signal);
+ isStatic = func->isStatic();
+ }
+ else if ((*c)->type() == Node::Variable) {
+ const VariableNode *var = static_cast(*c);
+ isStatic = var->isStatic();
+ }
+
+ switch ((*c)->access()) {
+ case Node::Public:
+ if (isSlot) {
+ insert(publicSlots, *c, style, status);
+ }
+ else if (isSignal) {
+ insert(publicSignals, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticPublicMembers,*c,style,status);
+ }
+ else if ((*c)->type() == Node::Property) {
+ insert(properties, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(publicVariables, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ if (!insertReimpFunc(publicFunctions,*c,status))
+ insert(publicFunctions, *c, style, status);
+ }
+ else {
+ insert(publicTypes, *c, style, status);
+ }
+ break;
+ case Node::Protected:
+ if (isSlot) {
+ insert(protectedSlots, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticProtectedMembers,*c,style,status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(protectedVariables,*c,style,status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ if (!insertReimpFunc(protectedFunctions,*c,status))
+ insert(protectedFunctions, *c, style, status);
+ }
+ else {
+ insert(protectedTypes, *c, style, status);
+ }
+ break;
+ case Node::Private:
+ if (isSlot) {
+ insert(privateSlots, *c, style, status);
+ }
+ else if (isStatic) {
+ if ((*c)->type() != Node::Variable
+ || !(*c)->doc().isEmpty())
+ insert(staticPrivateMembers,*c,style,status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ if (!insertReimpFunc(privateFunctions,*c,status))
+ insert(privateFunctions, *c, style, status);
+ }
+ else {
+ insert(privateTypes,*c,style,status);
+ }
+ }
+ ++c;
+ }
+
+ QList::ConstIterator r =
+ ancestorClass->baseClasses().begin();
+ while (r != ancestorClass->baseClasses().end()) {
+ stack.prepend((*r).node);
+ ++r;
+ }
+ }
+
+ append(sections, publicTypes);
+ append(sections, properties);
+ append(sections, publicFunctions);
+ append(sections, publicSlots);
+ append(sections, publicSignals);
+ append(sections, publicVariables);
+ append(sections, staticPublicMembers);
+ append(sections, protectedTypes);
+ append(sections, protectedFunctions);
+ append(sections, protectedSlots);
+ append(sections, protectedVariables);
+ append(sections, staticProtectedMembers);
+ append(sections, privateTypes);
+ append(sections, privateFunctions);
+ append(sections, privateSlots);
+ append(sections, staticPrivateMembers);
+ append(sections, relatedNonMembers);
+ append(sections, macros);
+ }
+ else if (style == Detailed) {
+ FastSection memberFunctions(classe,"Member Function Documentation","func","member","members");
+ FastSection memberTypes(classe,"Member Type Documentation","types","member","members");
+ FastSection memberVariables(classe,"Member Variable Documentation","vars","member","members");
+ FastSection properties(classe,"Property Documentation","prop","member","members");
+ FastSection relatedNonMembers(classe,"Related Non-Members","relnonmem","member","members");
+ FastSection macros(classe,"Macro Documentation","macros","member","members");
+
+ NodeList::ConstIterator r = classe->relatedNodes().begin();
+ while (r != classe->relatedNodes().end()) {
+ if ((*r)->type() == Node::Function) {
+ FunctionNode *func = static_cast(*r);
+ if (func->isMacro())
+ insert(macros, *r, style, status);
+ else
+ insert(relatedNonMembers, *r, style, status);
+ }
+ else {
+ insert(relatedNonMembers, *r, style, status);
+ }
+ ++r;
+ }
+
+ NodeList::ConstIterator c = classe->childNodes().begin();
+ while (c != classe->childNodes().end()) {
+ if ((*c)->type() == Node::Enum ||
+ (*c)->type() == Node::Typedef) {
+ insert(memberTypes, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Property) {
+ insert(properties, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Variable) {
+ if (!(*c)->doc().isEmpty())
+ insert(memberVariables, *c, style, status);
+ }
+ else if ((*c)->type() == Node::Function) {
+ FunctionNode *function = static_cast(*c);
+ if (!function->associatedProperty())
+ insert(memberFunctions, function, style, status);
+ }
+ ++c;
+ }
+
+ append(sections, memberTypes);
+ append(sections, properties);
+ append(sections, memberFunctions);
+ append(sections, memberVariables);
+ append(sections, relatedNonMembers);
+ append(sections, macros);
+ }
+ else {
+ FastSection all(classe,"","","member","members");
+
+ QStack stack;
+ stack.push(classe);
+
+ while (!stack.isEmpty()) {
+ const ClassNode *ancestorClass = stack.pop();
+
+ NodeList::ConstIterator c = ancestorClass->childNodes().begin();
+ while (c != ancestorClass->childNodes().end()) {
+ if ((*c)->access() != Node::Private &&
+ (*c)->type() != Node::Property)
+ insert(all, *c, style, status);
+ ++c;
+ }
+
+ QList::ConstIterator r =
+ ancestorClass->baseClasses().begin();
+ while (r != ancestorClass->baseClasses().end()) {
+ stack.prepend((*r).node);
+ ++r;
+ }
+ }
+ append(sections, all);
+ }
+ }
+ else {
+ if (style == Summary || style == Detailed) {
+ FastSection namespaces(inner,
+ "Namespaces",
+ style == Detailed ? "nmspace" : "",
+ "namespace",
+ "namespaces");
+ FastSection classes(inner,
+ "Classes",
+ style == Detailed ? "classes" : "",
+ "class",
+ "classes");
+ FastSection types(inner,
+ style == Summary ? "Types" : "Type Documentation",
+ style == Detailed ? "types" : "",
+ "type",
+ "types");
+ FastSection functions(inner,
+ style == Summary ?
+ "Functions" : "Function Documentation",
+ style == Detailed ? "func" : "",
+ "function",
+ "functions");
+ FastSection macros(inner,
+ style == Summary ?
+ "Macros" : "Macro Documentation",
+ style == Detailed ? "macros" : "",
+ "macro",
+ "macros");
+
+ NodeList nodeList = inner->childNodes();
+ nodeList += inner->relatedNodes();
+
+ NodeList::ConstIterator n = nodeList.begin();
+ while (n != nodeList.end()) {
+ switch ((*n)->type()) {
+ case Node::Namespace:
+ insert(namespaces, *n, style, status);
+ break;
+ case Node::Class:
+ insert(classes, *n, style, status);
+ break;
+ case Node::Enum:
+ case Node::Typedef:
+ insert(types, *n, style, status);
+ break;
+ case Node::Function:
+ {
+ FunctionNode *func = static_cast(*n);
+ if (func->isMacro())
+ insert(macros, *n, style, status);
+ else
+ insert(functions, *n, style, status);
+ }
+ break;
+ default:
+ ;
+ }
+ ++n;
+ }
+ append(sections, namespaces);
+ append(sections, classes);
+ append(sections, types);
+ append(sections, functions);
+ append(sections, macros);
+ }
+ }
+
+ return sections;
+}
+
+const Node *CppCodeMarker::resolveTarget(const QString& target,
+ const Tree* tree,
+ const Node* relative,
+ const Node* self)
+{
+ if (target.endsWith("()")) {
+ const FunctionNode *func;
+ QString funcName = target;
+ funcName.chop(2);
+
+ QStringList path = funcName.split("::");
+ if ((func = tree->findFunctionNode(path,
+ relative,
+ Tree::SearchBaseClasses))
+ && func->metaness() != FunctionNode::MacroWithoutParams)
+ return func;
+ }
+ else if (target.contains(QLatin1Char('#'))) {
+ // ### this doesn't belong here; get rid of TargetNode hack
+ int hashAt = target.indexOf(QLatin1Char('#'));
+ QString link = target.left(hashAt);
+ QString ref = target.mid(hashAt + 1);
+ const Node *node;
+ if (link.isEmpty()) {
+ node = relative;
+ }
+ else {
+ QStringList path(link);
+ node = tree->findNode(path, tree->root(), Tree::SearchBaseClasses);
+ }
+ if (node && node->isInnerNode()) {
+ const Atom *atom = node->doc().body().firstAtom();
+ while (atom) {
+ if (atom->type() == Atom::Target && atom->string() == ref) {
+ Node *parentNode = const_cast(node);
+ return new TargetNode(static_cast(parentNode),
+ ref);
+ }
+ atom = atom->next();
+ }
+ }
+ }
+ else {
+ QStringList path = target.split("::");
+ const Node *node;
+ int flags = Tree::SearchBaseClasses |
+ Tree::SearchEnumValues |
+ Tree::NonFunction;
+ if ((node = tree->findNode(path,
+ relative,
+ flags,
+ self)))
+ return node;
+ }
+ return 0;
+}
+
+static const char * const typeTable[] = {
+ "bool", "char", "double", "float", "int", "long", "short",
+ "signed", "unsigned", "uint", "ulong", "ushort", "uchar", "void",
+ "qlonglong", "qulonglong",
+ "qint", "qint8", "qint16", "qint32", "qint64",
+ "quint", "quint8", "quint16", "quint32", "quint64",
+ "qreal", "cond", 0
+};
+
+static const char * const keywordTable[] = {
+ "and", "and_eq", "asm", "auto", "bitand", "bitor", "break",
+ "case", "catch", "class", "compl", "const", "const_cast",
+ "continue", "default", "delete", "do", "dynamic_cast", "else",
+ "enum", "explicit", "export", "extern", "false", "for", "friend",
+ "goto", "if", "include", "inline", "monitor", "mutable", "namespace",
+ "new", "not", "not_eq", "operator", "or", "or_eq", "private", "protected",
+ "public", "register", "reinterpret_cast", "return", "sizeof",
+ "static", "static_cast", "struct", "switch", "template", "this",
+ "throw", "true", "try", "typedef", "typeid", "typename", "union",
+ "using", "virtual", "volatile", "wchar_t", "while", "xor",
+ "xor_eq", "synchronized",
+ // Qt specific
+ "signals", "slots", "emit", 0
+};
+
+/*
+ @char
+ @class
+ @comment
+ @function
+ @keyword
+ @number
+ @op
+ @preprocessor
+ @string
+ @type
+*/
+
+QString CppCodeMarker::addMarkUp(const QString &in,
+ const Node * /* relative */,
+ const Location & /* location */)
+{
+#define readChar() \
+ ch = (i < (int)code.length()) ? code[i++].cell() : EOF
+
+ QString code = in;
+
+ QMap types;
+ QMap keywords;
+ int j = 0;
+ while (typeTable[j] != 0) {
+ types.insert(QString(typeTable[j]), 0);
+ j++;
+ }
+ j = 0;
+ while (keywordTable[j] != 0) {
+ keywords.insert(QString(keywordTable[j]), 0);
+ j++;
+ }
+
+ QString out("");
+ int braceDepth = 0;
+ int parenDepth = 0;
+ int i = 0;
+ int start = 0;
+ int finish = 0;
+ QChar ch;
+ QRegExp classRegExp("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)");
+ QRegExp functionRegExp("q([A-Z][a-z]+)+");
+
+ readChar();
+
+ while (ch != EOF) {
+ QString tag;
+ bool target = false;
+
+ if (ch.isLetter() || ch == '_') {
+ QString ident;
+ do {
+ ident += ch;
+ finish = i;
+ readChar();
+ } while (ch.isLetterOrNumber() || ch == '_');
+
+ if (classRegExp.exactMatch(ident)) {
+ tag = QLatin1String("type");
+ } else if (functionRegExp.exactMatch(ident)) {
+ tag = QLatin1String("func");
+ target = true;
+ } else if (types.contains(ident)) {
+ tag = QLatin1String("type");
+ } else if (keywords.contains(ident)) {
+ tag = QLatin1String("keyword");
+ } else if (braceDepth == 0 && parenDepth == 0) {
+ if (QString(code.unicode() + i - 1, code.length() - (i - 1))
+ .indexOf(QRegExp(QLatin1String("^\\s*\\("))) == 0)
+ tag = QLatin1String("func");
+ target = true;
+ }
+ } else if (ch.isDigit()) {
+ do {
+ finish = i;
+ readChar();
+ } while (ch.isLetterOrNumber() || ch == '.');
+ tag = QLatin1String("number");
+ } else {
+ switch (ch.unicode()) {
+ case '+':
+ case '-':
+ case '!':
+ case '%':
+ case '^':
+ case '&':
+ case '*':
+ case ',':
+ case '.':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '[':
+ case ']':
+ case '|':
+ case '~':
+ finish = i;
+ readChar();
+ tag = QLatin1String("op");
+ break;
+ case '"':
+ finish = i;
+ readChar();
+
+ while (ch != EOF && ch != '"') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QLatin1String("string");
+ break;
+ case '#':
+ finish = i;
+ readChar();
+ while (ch != EOF && ch != '\n') {
+ if (ch == '\\')
+ readChar();
+ finish = i;
+ readChar();
+ }
+ tag = QLatin1String("preprocessor");
+ break;
+ case '\'':
+ finish = i;
+ readChar();
+
+ while (ch != EOF && ch != '\'') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QLatin1String("char");
+ break;
+ case '(':
+ finish = i;
+ readChar();
+ parenDepth++;
+ break;
+ case ')':
+ finish = i;
+ readChar();
+ parenDepth--;
+ break;
+ case ':':
+ finish = i;
+ readChar();
+ if (ch == ':') {
+ finish = i;
+ readChar();
+ tag = QLatin1String("op");
+ }
+ break;
+ case '/':
+ finish = i;
+ readChar();
+ if (ch == '/') {
+ do {
+ finish = i;
+ readChar();
+ } while (ch != EOF && ch != '\n');
+ tag = QLatin1String("comment");
+ } else if (ch == '*') {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ finish = i;
+ readChar();
+
+ while (!metAsterSlash) {
+ if (ch == EOF)
+ break;
+
+ if (ch == '*')
+ metAster = true;
+ else if (metAster && ch == '/')
+ metAsterSlash = true;
+ else
+ metAster = false;
+ finish = i;
+ readChar();
+ }
+ tag = QLatin1String("comment");
+ } else {
+ tag = QLatin1String("op");
+ }
+ break;
+ case '{':
+ finish = i;
+ readChar();
+ braceDepth++;
+ break;
+ case '}':
+ finish = i;
+ readChar();
+ braceDepth--;
+ break;
+ default:
+ finish = i;
+ readChar();
+ }
+ }
+
+ QString text;
+ text = code.mid(start, finish - start);
+ start = finish;
+
+ if (!tag.isEmpty()) {
+ out += QLatin1String("<@") + tag;
+ if (target)
+ out += QLatin1String(" target=\"") + text + QLatin1String("()\"");
+ out += QLatin1Char('>');
+ }
+
+ out += protect(text);
+
+ if (!tag.isEmpty())
+ out += QLatin1String("@") + tag + QLatin1Char('>');
+ }
+
+ if (start < code.length()) {
+ out += protect(code.mid(start));
+ }
+
+ return out;
+}
+
+/*!
+ This function is for documenting QML properties. It returns
+ the list of documentation sections for the children of the
+ \a qmlClassNode.
+
+ Currently, it only handles QML property groups.
+ */
+QList CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode,
+ SynopsisStyle style)
+{
+ QList sections;
+ if (qmlClassNode) {
+ if (style == Summary) {
+ FastSection qmlproperties(qmlClassNode,
+ "Properties",
+ "",
+ "property",
+ "properties");
+ FastSection qmlattachedproperties(qmlClassNode,
+ "Attached Properties",
+ "",
+ "property",
+ "properties");
+ FastSection qmlsignals(qmlClassNode,
+ "Signals",
+ "",
+ "signal",
+ "signals");
+ FastSection qmlsignalhandlers(qmlClassNode,
+ "Signal Handlers",
+ "",
+ "signal handler",
+ "signal handlers");
+ FastSection qmlattachedsignals(qmlClassNode,
+ "Attached Signals",
+ "",
+ "signal",
+ "signals");
+ FastSection qmlmethods(qmlClassNode,
+ "Methods",
+ "",
+ "method",
+ "methods");
+ FastSection qmlattachedmethods(qmlClassNode,
+ "Attached Methods",
+ "",
+ "method",
+ "methods");
+
+ const QmlClassNode* qcn = qmlClassNode;
+ while (qcn != 0) {
+ NodeList::ConstIterator c = qcn->childNodes().begin();
+ while (c != qcn->childNodes().end()) {
+ if ((*c)->subType() == Node::QmlPropertyGroup) {
+ const QmlPropGroupNode* qpgn = static_cast(*c);
+ NodeList::ConstIterator p = qpgn->childNodes().begin();
+ while (p != qpgn->childNodes().end()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(*p);
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*p,style,Okay);
+ else
+ insert(qmlproperties,*p,style,Okay);
+ }
+ ++p;
+ }
+ }
+ else if ((*c)->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(*c);
+ if (pn->qmlPropNodes().isEmpty()) {
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*c,style,Okay);
+ else
+ insert(qmlproperties,*c,style,Okay);
+ }
+ else {
+ NodeList::ConstIterator p = pn->qmlPropNodes().begin();
+ while (p != pn->qmlPropNodes().end()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(*p);
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*p,style,Okay);
+ else
+ insert(qmlproperties,*p,style,Okay);
+ }
+ ++p;
+ }
+ }
+ }
+ else if ((*c)->type() == Node::QmlSignal) {
+ const FunctionNode* sn = static_cast(*c);
+ if (sn->isAttached())
+ insert(qmlattachedsignals,*c,style,Okay);
+ else
+ insert(qmlsignals,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlSignalHandler) {
+ insert(qmlsignalhandlers,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlMethod) {
+ const FunctionNode* mn = static_cast(*c);
+ if (mn->isAttached())
+ insert(qmlattachedmethods,*c,style,Okay);
+ else
+ insert(qmlmethods,*c,style,Okay);
+ }
+ ++c;
+ }
+ if (qcn->qmlBase() != 0) {
+ qcn = static_cast(qcn->qmlBase());
+ if (!qcn->isAbstract())
+ qcn = 0;
+ }
+ else
+ qcn = 0;
+ }
+ append(sections,qmlproperties);
+ append(sections,qmlattachedproperties);
+ append(sections,qmlsignals);
+ append(sections,qmlsignalhandlers);
+ append(sections,qmlattachedsignals);
+ append(sections,qmlmethods);
+ append(sections,qmlattachedmethods);
+ }
+ else if (style == Detailed) {
+ FastSection qmlproperties(qmlClassNode, "Property Documentation","qmlprop","member","members");
+ FastSection qmlattachedproperties(qmlClassNode,"Attached Property Documentation","qmlattprop",
+ "member","members");
+ FastSection qmlsignals(qmlClassNode,"Signal Documentation","qmlsig","signal","signals");
+ FastSection qmlsignalhandlers(qmlClassNode,"Signal Handler Documentation","qmlsighan","signal handler","signal handlers");
+ FastSection qmlattachedsignals(qmlClassNode,"Attached Signal Documentation","qmlattsig",
+ "signal","signals");
+ FastSection qmlmethods(qmlClassNode,"Method Documentation","qmlmeth","member","members");
+ FastSection qmlattachedmethods(qmlClassNode,"Attached Method Documentation","qmlattmeth",
+ "member","members");
+ const QmlClassNode* qcn = qmlClassNode;
+ while (qcn != 0) {
+ NodeList::ConstIterator c = qcn->childNodes().begin();
+ while (c != qcn->childNodes().end()) {
+ if ((*c)->subType() == Node::QmlPropertyGroup) {
+ const QmlPropGroupNode* pgn = static_cast(*c);
+ if (pgn->isAttached())
+ insert(qmlattachedproperties,*c,style,Okay);
+ else
+ insert(qmlproperties,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlProperty) {
+ const QmlPropertyNode* pn = static_cast(*c);
+ if (pn->isAttached())
+ insert(qmlattachedproperties,*c,style,Okay);
+ else
+ insert(qmlproperties,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlSignal) {
+ const FunctionNode* sn = static_cast(*c);
+ if (sn->isAttached())
+ insert(qmlattachedsignals,*c,style,Okay);
+ else
+ insert(qmlsignals,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlSignalHandler) {
+ insert(qmlsignalhandlers,*c,style,Okay);
+ }
+ else if ((*c)->type() == Node::QmlMethod) {
+ const FunctionNode* mn = static_cast(*c);
+ if (mn->isAttached())
+ insert(qmlattachedmethods,*c,style,Okay);
+ else
+ insert(qmlmethods,*c,style,Okay);
+ }
+ ++c;
+ }
+ if (qcn->qmlBase() != 0) {
+ qcn = static_cast(qcn->qmlBase());
+ if (!qcn->isAbstract())
+ qcn = 0;
+ }
+ else
+ qcn = 0;
+ }
+ append(sections,qmlproperties);
+ append(sections,qmlattachedproperties);
+ append(sections,qmlsignals);
+ append(sections,qmlsignalhandlers);
+ append(sections,qmlattachedsignals);
+ append(sections,qmlmethods);
+ append(sections,qmlattachedmethods);
+ }
+ else {
+ FastSection all(qmlClassNode,"","","member","members");
+
+ const QmlClassNode* current = qmlClassNode;
+ while (current != 0) {
+ NodeList::ConstIterator c = current->childNodes().begin();
+ while (c != current->childNodes().end()) {
+ if ((*c)->subType() == Node::QmlPropertyGroup) {
+ const QmlPropGroupNode* qpgn = static_cast(*c);
+ NodeList::ConstIterator p = qpgn->childNodes().begin();
+ while (p != qpgn->childNodes().end()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ QString key = current->name() + "::" + (*p)->name();
+ key = sortName(*p, &key);
+ if (!all.memberMap.contains(key))
+ all.memberMap.insert(key,*p);
+ //insert(all,*p,style,Okay);
+ }
+ ++p;
+ }
+ }
+ else {
+ QString key = current->name() + "::" + (*c)->name();
+ key = sortName(*c, &key);
+ if (!all.memberMap.contains(key))
+ all.memberMap.insert(key,*c);
+ //insert(all,*c,style,Okay);
+ }
+ ++c;
+ }
+ const FakeNode* fn = current->qmlBase();
+ if (fn) {
+ if (fn->subType() == Node::QmlClass)
+ current = static_cast(fn);
+ else {
+ fn->doc().location().warning(tr("Base class of QML class '%1' is ambgiguous")
+ .arg(current->name()));
+ current = 0;
+ }
+ }
+ else
+ current = 0;
+ }
+ append(sections, all, true);
+ }
+ }
+
+ return sections;
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/cppcodemarker.h b/src/tools/qdoc/cppcodemarker.h
new file mode 100644
index 0000000..b898f9e
--- /dev/null
+++ b/src/tools/qdoc/cppcodemarker.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodemarker.h
+*/
+
+#ifndef CPPCODEMARKER_H
+#define CPPCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class CppCodeMarker : public CodeMarker
+{
+public:
+ CppCodeMarker();
+ ~CppCodeMarker();
+
+ virtual bool recognizeCode(const QString& code);
+ virtual bool recognizeExtension(const QString& ext);
+ virtual bool recognizeLanguage(const QString& lang);
+ virtual Atom::Type atomType() const;
+ virtual QString plainName(const Node *node);
+ virtual QString plainFullName(const Node *node, const Node *relative);
+ virtual QString markedUpCode(const QString& code,
+ const Node *relative,
+ const Location &location);
+ virtual QString markedUpSynopsis(const Node *node,
+ const Node *relative,
+ SynopsisStyle style);
+ virtual QString markedUpQmlItem(const Node *node, bool summary);
+ virtual QString markedUpName(const Node *node);
+ virtual QString markedUpFullName(const Node *node, const Node *relative);
+ virtual QString markedUpEnumValue(const QString &enumValue, const Node *relative);
+ virtual QString markedUpIncludes(const QStringList& includes);
+ virtual QString functionBeginRegExp(const QString& funcName);
+ virtual QString functionEndRegExp(const QString& funcName);
+ virtual QList sections(const InnerNode *innerNode,
+ SynopsisStyle style,
+ Status status);
+ virtual QList qmlSections(const QmlClassNode* qmlClassNode,
+ SynopsisStyle style);
+ virtual const Node* resolveTarget(const QString& target,
+ const Tree* tree,
+ const Node* relative,
+ const Node* self = 0);
+
+private:
+ QString addMarkUp(const QString& protectedCode,
+ const Node *relative,
+ const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp
new file mode 100644
index 0000000..b83e617
--- /dev/null
+++ b/src/tools/qdoc/cppcodeparser.cpp
@@ -0,0 +1,2502 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodeparser.cpp
+*/
+
+#include
+#include
+#include
+#include "codechunk.h"
+#include "config.h"
+#include "cppcodeparser.h"
+#include "tokenizer.h"
+#include "tree.h"
+#include
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+#define COMMAND_CLASS Doc::alias("class")
+#define COMMAND_CONTENTSPAGE Doc::alias("contentspage")
+#define COMMAND_DITAMAP Doc::alias("ditamap")
+#define COMMAND_ENUM Doc::alias("enum")
+#define COMMAND_EXAMPLE Doc::alias("example")
+#define COMMAND_EXTERNALPAGE Doc::alias("externalpage")
+#define COMMAND_FILE Doc::alias("file")
+#define COMMAND_FN Doc::alias("fn")
+#define COMMAND_GROUP Doc::alias("group")
+#define COMMAND_HEADERFILE Doc::alias("headerfile")
+#define COMMAND_INDEXPAGE Doc::alias("indexpage")
+#define COMMAND_INHEADERFILE Doc::alias("inheaderfile")
+#define COMMAND_MACRO Doc::alias("macro")
+#define COMMAND_MODULE Doc::alias("module")
+#define COMMAND_NAMESPACE Doc::alias("namespace")
+#define COMMAND_OVERLOAD Doc::alias("overload")
+#define COMMAND_NEXTPAGE Doc::alias("nextpage")
+#define COMMAND_PAGE Doc::alias("page")
+#define COMMAND_PREVIOUSPAGE Doc::alias("previouspage")
+#define COMMAND_PROPERTY Doc::alias("property")
+#define COMMAND_REIMP Doc::alias("reimp")
+#define COMMAND_RELATES Doc::alias("relates")
+#define COMMAND_SERVICE Doc::alias("service")
+#define COMMAND_STARTPAGE Doc::alias("startpage")
+#define COMMAND_TYPEDEF Doc::alias("typedef")
+#define COMMAND_VARIABLE Doc::alias("variable")
+#define COMMAND_QMLABSTRACT Doc::alias("qmlabstract")
+#define COMMAND_QMLCLASS Doc::alias("qmlclass")
+#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")
+#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty")
+#define COMMAND_QMLINHERITS Doc::alias("inherits")
+#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal")
+#define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal")
+#define COMMAND_QMLMETHOD Doc::alias("qmlmethod")
+#define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod")
+#define COMMAND_QMLDEFAULT Doc::alias("default")
+#define COMMAND_QMLREADONLY Doc::alias("readonly")
+#define COMMAND_QMLBASICTYPE Doc::alias("qmlbasictype")
+#define COMMAND_QMLMODULE Doc::alias("qmlmodule")
+#define COMMAND_AUDIENCE Doc::alias("audience")
+#define COMMAND_CATEGORY Doc::alias("category")
+#define COMMAND_PRODNAME Doc::alias("prodname")
+#define COMMAND_COMPONENT Doc::alias("component")
+#define COMMAND_AUTHOR Doc::alias("author")
+#define COMMAND_PUBLISHER Doc::alias("publisher")
+#define COMMAND_COPYRYEAR Doc::alias("copyryear")
+#define COMMAND_COPYRHOLDER Doc::alias("copyrholder")
+#define COMMAND_PERMISSIONS Doc::alias("permissions")
+#define COMMAND_LIFECYCLEVERSION Doc::alias("lifecycleversion")
+#define COMMAND_LIFECYCLEWSTATUS Doc::alias("lifecyclestatus")
+#define COMMAND_LICENSEYEAR Doc::alias("licenseyear")
+#define COMMAND_LICENSENAME Doc::alias("licensename")
+#define COMMAND_LICENSEDESCRIPTION Doc::alias("licensedescription")
+#define COMMAND_RELEASEDATE Doc::alias("releasedate")
+
+QStringList CppCodeParser::exampleFiles;
+QStringList CppCodeParser::exampleDirs;
+
+/*
+ This is used for fuzzy matching only, which in turn is only used
+ for Qt Jambi.
+*/
+static QString cleanType(const QString &type, const Tree *tree)
+{
+ QString result = type;
+ result.replace("qlonglong", "long long");
+ result.replace("qulonglong", "unsigned long long");
+ result.replace("qreal", "double");
+ result.replace(QRegExp("\\bu(int|short|char|long)\\b"), "unsigned \\1");
+ result.replace("QRgb", "unsigned int");
+ result.replace(" >", ">");
+ result.remove(" const[]");
+ result.replace("QStringList", "QStringList");
+ result.replace("qint8", "char");
+ result.replace("qint16", "short");
+ result.replace("qint32", "int");
+ result.replace("qint64", "long long");
+ result.replace("quint8", "unsigned char");
+ result.replace("quint16", "unsigned short");
+ result.replace("quint32", "unsigned int");
+ result.replace("quint64", "unsigned long long");
+
+ if (result.contains("QFlags")) {
+ QRegExp regExp("QFlags<(((?:[^<>]+::)*)([^<>:]+))>");
+ int pos = 0;
+ while ((pos = result.indexOf(regExp, pos)) != -1) {
+ // we assume that the path for the associated enum
+ // is the same as for the flag typedef
+ QStringList path = regExp.cap(2).split("::",
+ QString::SkipEmptyParts);
+ const EnumNode *enume = static_cast(
+ tree->findNode(QStringList(path) << regExp.cap(3),
+ Node::Enum));
+ if (enume && enume->flagsType())
+ result.replace(pos, regExp.matchedLength(),
+ (QStringList(path) << enume->flagsType()->name()).join("::"));
+ ++pos;
+ }
+ }
+ if (result.contains("::")) {
+ // remove needless (and needful) class prefixes
+ QRegExp regExp("[A-Za-z0-9_]+::");
+ result.remove(regExp);
+ }
+ return result;
+}
+
+/*!
+ The constructor initializes some regular expressions
+ and calls reset().
+ */
+CppCodeParser::CppCodeParser()
+ : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
+{
+ reset(0);
+}
+
+/*!
+ The destructor is trivial.
+ */
+CppCodeParser::~CppCodeParser()
+{
+ // nothing.
+}
+
+/*!
+ The constructor initializes a map of special node types
+ for identifying important nodes. And it initializes
+ some filters for identifying certain kinds of files.
+ */
+void CppCodeParser::initializeParser(const Config &config)
+{
+ CodeParser::initializeParser(config);
+
+ nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
+ nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
+ nodeTypeMap.insert(COMMAND_SERVICE, Node::Class);
+ nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
+ nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
+ nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
+ nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
+
+ exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES);
+ exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
+ QStringList exampleFilePatterns = config.getStringList(
+ CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);
+
+ if (!exampleFilePatterns.isEmpty())
+ exampleNameFilter = exampleFilePatterns.join(" ");
+ else
+ exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
+
+ QStringList exampleImagePatterns = config.getStringList(
+ CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
+
+ if (!exampleImagePatterns.isEmpty())
+ exampleImageFilter = exampleImagePatterns.join(" ");
+ else
+ exampleImageFilter = "*.png";
+}
+
+/*!
+ Clear the map of common node types and call
+ the same function in the base class.
+ */
+void CppCodeParser::terminateParser()
+{
+ nodeTypeMap.clear();
+ CodeParser::terminateParser();
+}
+
+/*!
+ Returns "Cpp".
+ */
+QString CppCodeParser::language()
+{
+ return "Cpp";
+}
+
+/*!
+ Returns a list of extensions for header files.
+ */
+QStringList CppCodeParser::headerFileNameFilter()
+{
+ return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx";
+}
+
+/*!
+ Returns a list of extensions for source files, i.e. not
+ header files.
+ */
+QStringList CppCodeParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm";
+}
+
+/*!
+ Parse the C++ header file identified by \a filePath
+ and add the parsed contents to the big \a tree. The
+ \a location is used for reporting errors.
+ */
+void CppCodeParser::parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ QFile in(filePath);
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
+ return;
+ }
+ createOutputSubdirectory(location, filePath);
+
+ reset(tree);
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+ matchDeclList(tree->root());
+ if (!fileTokenizer.version().isEmpty())
+ tree->setVersion(fileTokenizer.version());
+ in.close();
+
+ if (fileLocation.fileName() == "qiterator.h")
+ parseQiteratorDotH(location, filePath);
+}
+
+/*!
+ Get ready to parse the C++ cpp file identified by \a filePath
+ and add its parsed contents to the big \a tree. \a location is
+ used for reporting errors.
+
+ Call matchDocsAndStuff() to do all the parsing and tree building.
+ */
+void CppCodeParser::parseSourceFile(const Location& location,
+ const QString& filePath,
+ Tree *tree)
+{
+ QFile in(filePath);
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath).arg(strerror(errno)));
+ return;
+ }
+ createOutputSubdirectory(location, filePath);
+
+ reset(tree);
+ Location fileLocation(filePath);
+ Tokenizer fileTokenizer(fileLocation, in);
+ tokenizer = &fileTokenizer;
+ readToken();
+ usedNamespaces.clear();
+ matchDocsAndStuff();
+ in.close();
+}
+
+/*!
+ This is called after all the header files have been parsed.
+ I think the most important thing it does is resolve class
+ inheritance links in the tree. But it also initializes a
+ bunch of stuff.
+ */
+void CppCodeParser::doneParsingHeaderFiles(Tree *tree)
+{
+ tree->resolveInheritance();
+
+ QMapIterator i(sequentialIteratorClasses);
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ sequentialIteratorDefinition,
+ tree);
+ }
+ i = mutableSequentialIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ mutableSequentialIteratorDefinition,
+ tree);
+ }
+ i = associativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ associativeIteratorDefinition,
+ tree);
+ }
+ i = mutableAssociativeIteratorClasses;
+ while (i.hasNext()) {
+ i.next();
+ instantiateIteratorMacro(i.key(),
+ i.value(),
+ mutableAssociativeIteratorDefinition,
+ tree);
+ }
+ sequentialIteratorDefinition.clear();
+ mutableSequentialIteratorDefinition.clear();
+ associativeIteratorDefinition.clear();
+ mutableAssociativeIteratorDefinition.clear();
+ sequentialIteratorClasses.clear();
+ mutableSequentialIteratorClasses.clear();
+ associativeIteratorClasses.clear();
+ mutableAssociativeIteratorClasses.clear();
+}
+
+/*!
+ This is called after all the source files (i.e., not the
+ header files) have been parsed. It traverses the tree to
+ resolve property links, normalize overload signatures, and
+ do other housekeeping of the tree.
+ */
+void CppCodeParser::doneParsingSourceFiles(Tree *tree)
+{
+ tree->root()->makeUndocumentedChildrenInternal();
+ tree->root()->clearCurrentChildPointers();
+ tree->root()->normalizeOverloads();
+ tree->fixInheritance();
+ tree->resolveProperties();
+}
+
+/*!
+ This function searches the \a tree to find a FunctionNode
+ for a function with the signature \a synopsis. If the
+ \a relative node is provided, the search begins there. If
+ \a fuzzy is true, base classes are searched. The function
+ node is returned, if found.
+ */
+const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,
+ Tree *tree,
+ Node *relative,
+ bool fuzzy)
+{
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+ int flags = fuzzy ? int(Tree::SearchBaseClasses) : 0;
+
+ reset(tree);
+ if (makeFunctionNode(synopsis, &parentPath, &clone)) {
+ func = tree->findFunctionNode(parentPath, clone, relative, flags);
+
+ /*
+ This is necessary because Roberto's parser resolves typedefs.
+ */
+ if (!func && fuzzy) {
+ func = tre->findFunctionNode(parentPath +
+ QStringList(clone->name()),
+ relative,
+ flags);
+ if (!func && clone->name().contains('_')) {
+ QStringList path = parentPath;
+ path << clone->name().split('_');
+ func = tre->findFunctionNode(path, relative, flags);
+ }
+
+ if (func) {
+ NodeList overloads = func->parent()->overloads(func->name());
+ NodeList candidates;
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast(overloads.at(i));
+ if (overload->status() != Node::Compat
+ && overload->parameters().count() == clone->parameters().count()
+ && !overload->isConst() == !clone->isConst())
+ candidates << overload;
+ }
+ if (candidates.count() == 0)
+ return 0;
+
+ /*
+ There's only one function with the correct number
+ of parameters. That must be the one.
+ */
+ if (candidates.count() == 1)
+ return static_cast(candidates.first());
+
+ overloads = candidates;
+ candidates.clear();
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast(overloads.at(i));
+ QList params1 = overload->parameters();
+ QList params2 = clone->parameters();
+
+ int j;
+ for (j = 0; j < params1.count(); ++j) {
+ if (!params2.at(j).name().startsWith(params1.at(j).name()))
+ break;
+ }
+ if (j == params1.count())
+ candidates << overload;
+ }
+
+ /*
+ There are several functions with the correct
+ parameter count, but only one has the correct
+ parameter names.
+ */
+ if (candidates.count() == 1)
+ return static_cast(candidates.first());
+
+ candidates.clear();
+ for (int i = 0; i < overloads.count(); ++i) {
+ FunctionNode *overload = static_cast(overloads.at(i));
+ QList params1 = overload->parameters();
+ QList params2 = clone->parameters();
+
+ int j;
+ for (j = 0; j < params1.count(); ++j) {
+ if (params1.at(j).rightType() != params2.at(j).rightType())
+ break;
+
+ if (cleanType(params1.at(j).leftType(), tree)
+ != cleanType(params2.at(j).leftType(), tree))
+ break;
+ }
+ if (j == params1.count())
+ candidates << overload;
+ }
+
+
+ /*
+ There are several functions with the correct
+ parameter count, but only one has the correct
+ types, loosely compared.
+ */
+ if (candidates.count() == 1)
+ return static_cast(candidates.first());
+
+ return 0;
+ }
+ }
+ delete clone;
+ }
+ return func;
+}
+
+/*!
+ Returns the set of strings reopresenting the topic commands.
+ */
+QSet CppCodeParser::topicCommands()
+{
+ return QSet() << COMMAND_CLASS
+ << COMMAND_DITAMAP
+ << COMMAND_ENUM
+ << COMMAND_EXAMPLE
+ << COMMAND_EXTERNALPAGE
+ << COMMAND_FILE
+ << COMMAND_FN
+ << COMMAND_GROUP
+ << COMMAND_HEADERFILE
+ << COMMAND_MACRO
+ << COMMAND_MODULE
+ << COMMAND_NAMESPACE
+ << COMMAND_PAGE
+ << COMMAND_PROPERTY
+ << COMMAND_SERVICE
+ << COMMAND_TYPEDEF
+ << COMMAND_VARIABLE
+ << COMMAND_QMLCLASS
+ << COMMAND_QMLPROPERTY
+ << COMMAND_QMLATTACHEDPROPERTY
+ << COMMAND_QMLSIGNAL
+ << COMMAND_QMLATTACHEDSIGNAL
+ << COMMAND_QMLMETHOD
+ << COMMAND_QMLATTACHEDMETHOD
+ << COMMAND_QMLBASICTYPE
+ << COMMAND_QMLMODULE;
+}
+
+/*!
+ Process the topic \a command in context \a doc with argument \a arg.
+ */
+Node* CppCodeParser::processTopicCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg)
+{
+ if (command == COMMAND_FN) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+ FunctionNode *clone = 0;
+
+ if (!makeFunctionNode(arg, &parentPath, &clone) &&
+ !makeFunctionNode("void " + arg, &parentPath, &clone)) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN));
+ }
+ else {
+ if (!usedNamespaces.isEmpty()) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + parentPath;
+ func = tre->findFunctionNode(newPath, clone);
+ if (func)
+ break;
+ }
+ }
+ // Search the root namespace if no match was found.
+ if (func == 0)
+ func = tre->findFunctionNode(parentPath, clone);
+
+ if (func == 0) {
+ if (parentPath.isEmpty() && !lastPath.isEmpty())
+ func = tre->findFunctionNode(lastPath, clone);
+ if (func == 0) {
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'")
+ .arg(clone->name() + "(...)")
+ .arg(COMMAND_FN),
+ tr("I cannot find any function of that name with the "
+ "specified signature. Make sure that the signature "
+ "is identical to the declaration, including 'const' "
+ "qualifiers."));
+ }
+ else {
+ doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")
+ .arg(lastPath.join("::"))
+ .arg(clone->name() + "()")
+ .arg(COMMAND_FN));
+ }
+ }
+ else {
+ lastPath = parentPath;
+ }
+ if (func) {
+ func->borrowParameterNames(clone);
+ func->setParentPath(clone->parentPath());
+ }
+ delete clone;
+ }
+ return func;
+ }
+ else if (command == COMMAND_MACRO) {
+ QStringList parentPath;
+ FunctionNode *func = 0;
+
+ if (makeFunctionNode(arg, &parentPath, &func, tre->root())) {
+ if (!parentPath.isEmpty()) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(COMMAND_MACRO));
+ delete func;
+ func = 0;
+ }
+ else {
+ func->setMetaness(FunctionNode::MacroWithParams);
+ QList params = func->parameters();
+ for (int i = 0; i < params.size(); ++i) {
+ Parameter ¶m = params[i];
+ if (param.name().isEmpty() && !param.leftType().isEmpty()
+ && param.leftType() != "...")
+ param = Parameter("", "", param.leftType());
+ }
+ func->setParameters(params);
+ }
+ return func;
+ }
+ else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg)) {
+ func = new FunctionNode(tre->root(), arg);
+ func->setAccess(Node::Public);
+ func->setLocation(doc.location());
+ func->setMetaness(FunctionNode::MacroWithoutParams);
+ }
+ else {
+ doc.location().warning(tr("Invalid syntax in '\\%1'")
+ .arg(COMMAND_MACRO));
+
+ }
+ return func;
+ }
+ else if (nodeTypeMap.contains(command)) {
+ /*
+ The command was neither "fn" nor "macro" .
+ */
+ // ### split(QLatin1Char(' ')) hack is there to support header file syntax
+ QStringList paths = arg.split(QLatin1Char(' '));
+ QStringList path = paths[0].split("::");
+ Node *node = 0;
+ if (!usedNamespaces.isEmpty()) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + path;
+ node = tre->findNode(newPath, nodeTypeMap[command]);
+ if (node) {
+ path = newPath;
+ break;
+ }
+ }
+ }
+ // Search the root namespace if no match was found.
+ if (node == 0)
+ node = tre->findNode(path, nodeTypeMap[command]);
+
+ if (node == 0) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
+ .arg(arg).arg(command));
+ lastPath = path;
+
+ }
+ else if (command == COMMAND_SERVICE) {
+ // If the command is "\service", then we need to tag the
+ // class with the actual service name.
+ QStringList args = arg.split(QLatin1Char(' '));
+ if (args.size() > 1) {
+ ClassNode *cnode = static_cast(node);
+ cnode->setServiceName(args[1]);
+ cnode->setHideFromMainList(true);
+ }
+ }
+ else if (node->isInnerNode()) {
+ if (path.size() > 1) {
+ path.pop_back();
+ usedNamespaces.insert(path.join("::"));
+ }
+ }
+#if 0
+ /*
+ This code apparently does nothing. After further
+ investigation to verify it is useless, it will
+ be removed.
+ */
+ if (command == COMMAND_CLASS) {
+ if (paths.size() > 1) {
+ if (!paths[1].endsWith(".h")) {
+ ClassNode* cnode = static_cast(node);
+ cnode->setQmlElement(paths[1]);
+ }
+ }
+ }
+#endif
+ return node;
+ }
+ else if (command == COMMAND_EXAMPLE) {
+ ExampleNode* en = new ExampleNode(tre->root(), arg);
+ createExampleFileNodes(en);
+ return en;
+ }
+ else if (command == COMMAND_EXTERNALPAGE) {
+ return new FakeNode(tre->root(), arg, Node::ExternalPage, Node::ArticlePage);
+ }
+ else if (command == COMMAND_FILE) {
+ return new FakeNode(tre->root(), arg, Node::File, Node::NoPageType);
+ }
+ else if (command == COMMAND_GROUP) {
+ return new FakeNode(tre->root(), arg, Node::Group, Node::OverviewPage);
+ }
+ else if (command == COMMAND_HEADERFILE) {
+ return new FakeNode(tre->root(), arg, Node::HeaderFile, Node::ApiPage);
+ }
+ else if (command == COMMAND_MODULE) {
+ return new FakeNode(tre->root(), arg, Node::Module, Node::OverviewPage);
+ }
+ else if (command == COMMAND_QMLMODULE) {
+ return new FakeNode(tre->root(), arg, Node::QmlModule, Node::OverviewPage);
+ }
+ else if (command == COMMAND_PAGE) {
+ Node::PageType ptype = Node::ArticlePage;
+ QStringList args = arg.split(QLatin1Char(' '));
+ if (args.size() > 1) {
+ QString t = args[1].toLower();
+ if (t == "howto")
+ ptype = Node::HowToPage;
+ else if (t == "api")
+ ptype = Node::ApiPage;
+ else if (t == "example")
+ ptype = Node::ExamplePage;
+ else if (t == "overview")
+ ptype = Node::OverviewPage;
+ else if (t == "tutorial")
+ ptype = Node::TutorialPage;
+ else if (t == "faq")
+ ptype = Node::FAQPage;
+ else if (t == "ditamap")
+ ptype = Node::DitaMapPage;
+ }
+
+ /*
+ Search for a node with the same name. If there is one,
+ then there is a collision, so create a collision node
+ and make the existing node a child of the collision
+ node, and then create the new Page node and make
+ it a child of the collision node as well. Return the
+ collision node.
+
+ If there is no collision, just create a new Page
+ node and return that one.
+ */
+ NameCollisionNode* ncn = tre->checkForCollision(args[0]);
+ FakeNode* fn = 0;
+ if (ptype == Node::DitaMapPage)
+ fn = new DitaMapNode(tre->root(), args[0]);
+ else
+ fn = new FakeNode(tre->root(), args[0], Node::Page, ptype);
+ if (ncn) {
+ ncn->addCollision(fn);
+ }
+ return fn;
+ }
+ else if (command == COMMAND_DITAMAP) {
+ FakeNode* fn = new DitaMapNode(tre->root(), arg);
+ return fn;
+ }
+ else if (command == COMMAND_QMLCLASS) {
+ const ClassNode* classNode = 0;
+ QStringList names = arg.split(QLatin1Char(' '));
+ if (names.size() > 1) {
+ Node* n = tre->findNode(names[1].split("::"),Node::Class);
+ if (n) {
+ classNode = static_cast(n);
+ }
+ }
+ /*
+ Search for a node with the same name. If there is one,
+ then there is a collision, so create a collision node
+ and make the existing node a child of the collision
+ node, and then create the new QML class node and make
+ it a child of the collision node as well. Return the
+ collision node.
+
+ If there is no collision, just create a new QML class
+ node and return that one.
+ */
+ NameCollisionNode* ncn = tre->checkForCollision(names[0]);
+ QmlClassNode* qcn = new QmlClassNode(tre->root(), names[0], classNode);
+ if (ncn) {
+ ncn->addCollision(qcn);
+ }
+ return qcn;
+ }
+ else if (command == COMMAND_QMLBASICTYPE) {
+ return new QmlBasicTypeNode(tre->root(), arg);
+ }
+ else if ((command == COMMAND_QMLSIGNAL) ||
+ (command == COMMAND_QMLMETHOD) ||
+ (command == COMMAND_QMLATTACHEDSIGNAL) ||
+ (command == COMMAND_QMLATTACHEDMETHOD)) {
+ QString module;
+ QString element;
+ QString type;
+ if (splitQmlMethodArg(doc,arg,type,module,element)) {
+ QmlClassNode* qmlClass = tre->findQmlClassNode(module,element);
+ if (qmlClass) {
+ if (command == COMMAND_QMLSIGNAL)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,false,COMMAND_QMLSIGNAL);
+ else if (command == COMMAND_QMLATTACHEDSIGNAL)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,true,COMMAND_QMLATTACHEDSIGNAL);
+ else if (command == COMMAND_QMLMETHOD)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,false,COMMAND_QMLMETHOD);
+ else if (command == COMMAND_QMLATTACHEDMETHOD)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,true,COMMAND_QMLATTACHEDMETHOD);
+ else
+ return 0; // never get here.
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ A QML property argument has the form...
+
+ ::
+ ::::
+
+ This function splits the argument into one of those
+ two forms. The three part form is the old form, which
+ was used before the creation of QtQuick 2 and Qt
+ Components. A is the QML equivalent of a
+ C++ namespace. So this function splits \a arg on "::"
+ and stores the parts in \a type, \a module, \a element,
+ and \a name, and returns true. If any part other than
+ \a module is not found, a qdoc warning is emitted and
+ false is returned.
+
+ \note The two elements \e{Component} and \e{QtObject} never
+ have a module qualifier.
+ */
+bool CppCodeParser::splitQmlPropertyArg(const Doc& doc,
+ const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element,
+ QString& name)
+{
+ QStringList blankSplit = arg.split(QLatin1Char(' '));
+ if (blankSplit.size() > 1) {
+ type = blankSplit[0];
+ QStringList colonSplit(blankSplit[1].split("::"));
+ if (colonSplit.size() == 3) {
+ module = colonSplit[0];
+ element = colonSplit[1];
+ name = colonSplit[2];
+ return true;
+ }
+ if (colonSplit.size() == 2) {
+ module.clear();
+ element = colonSplit[0];
+ name = colonSplit[1];
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ doc.location().warning(tr(msg.toLatin1().data()));
+ }
+ else {
+ QString msg = "Missing property type for " + arg;
+ doc.location().warning(tr(msg.toLatin1().data()));
+ }
+ return false;
+}
+
+/*!
+ A QML signal or method argument has the form...
+
+ ::(, , ...)
+ ::::(, , ...)
+
+ This function splits the argument into one of those two
+ forms, sets \a module, \a element, and \a name, and returns
+ true. If the argument doesn't match either form, an error
+ message is emitted and false is returned.
+
+ \note The two elements \e{Component} and \e{QtObject} never
+ have a module qualifier.
+ */
+bool CppCodeParser::splitQmlMethodArg(const Doc& doc,
+ const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element)
+{
+ QStringList colonSplit(arg.split("::"));
+ if (colonSplit.size() > 1) {
+ QStringList blankSplit = colonSplit[0].split(QLatin1Char(' '));
+ if (blankSplit.size() > 1) {
+ type = blankSplit[0];
+ if (colonSplit.size() > 2) {
+ module = blankSplit[1];
+ element = colonSplit[1];
+ }
+ else {
+ module.clear();
+ element = blankSplit[1];
+ }
+ }
+ else {
+ type = QString("");
+ if (colonSplit.size() > 2) {
+ module = colonSplit[0];
+ element = colonSplit[1];
+ }
+ else {
+ module.clear();
+ element = colonSplit[0];
+ }
+ }
+ return true;
+ }
+ QString msg = "Unrecognizable QML module/component qualifier for " + arg;
+ doc.location().warning(tr(msg.toLatin1().data()));
+ return false;
+}
+
+/*!
+ Process the topic \a command group with arguments \a args.
+
+ Currently, this function is called only for \e{qmlproperty}
+ and \e{qmlattachedproperty}.
+ */
+Node *CppCodeParser::processTopicCommandGroup(const Doc& doc,
+ const QString& command,
+ const QStringList& args)
+{
+ QmlPropGroupNode* qmlPropGroup = 0;
+ if ((command == COMMAND_QMLPROPERTY) ||
+ (command == COMMAND_QMLATTACHEDPROPERTY)) {
+ QString type;
+ QString module;
+ QString element;
+ QString property;
+ bool attached = (command == COMMAND_QMLATTACHEDPROPERTY);
+ QStringList::ConstIterator arg = args.begin();
+ if (splitQmlPropertyArg(doc,(*arg),type,module,element,property)) {
+ QmlClassNode* qmlClass = tre->findQmlClassNode(module,element);
+ if (qmlClass) {
+ qmlPropGroup = new QmlPropGroupNode(qmlClass,property,attached);
+ }
+ }
+ if (qmlPropGroup) {
+ const ClassNode *correspondingClass = static_cast(qmlPropGroup->parent())->classNode();
+ QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached);
+
+ const PropertyNode *correspondingProperty = 0;
+ if (correspondingClass) {
+ correspondingProperty = qmlPropNode->correspondingProperty(tre);
+ }
+ if (correspondingProperty) {
+ bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
+ qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());
+ }
+ ++arg;
+ while (arg != args.end()) {
+ if (splitQmlPropertyArg(doc,(*arg),type,module,element,property)) {
+ QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup,
+ property,
+ type,
+ attached);
+ if (correspondingProperty) {
+ bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
+ qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());
+ }
+ }
+ ++arg;
+ }
+ }
+ }
+ return qmlPropGroup;
+}
+
+/*!
+ Returns the set of strings representing the common metacommands
+ plus some other metacommands.
+ */
+QSet CppCodeParser::otherMetaCommands()
+{
+ return commonMetaCommands() << COMMAND_INHEADERFILE
+ << COMMAND_OVERLOAD
+ << COMMAND_REIMP
+ << COMMAND_RELATES
+ << COMMAND_CONTENTSPAGE
+ << COMMAND_NEXTPAGE
+ << COMMAND_PREVIOUSPAGE
+ << COMMAND_INDEXPAGE
+ << COMMAND_STARTPAGE
+ << COMMAND_QMLINHERITS
+ << COMMAND_QMLDEFAULT
+ << COMMAND_QMLREADONLY
+ << COMMAND_QMLABSTRACT;
+}
+
+/*!
+ Process the metacommand \a command in the context of the
+ \a node associated with the topic command and the \a doc.
+ \a arg is the argument to the metacommand.
+ */
+void CppCodeParser::processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg,
+ Node *node)
+{
+ if (command == COMMAND_INHEADERFILE) {
+ if (node != 0 && node->isInnerNode()) {
+ ((InnerNode *) node)->addInclude(arg);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1'")
+ .arg(COMMAND_INHEADERFILE));
+ }
+ }
+ else if (command == COMMAND_OVERLOAD) {
+ if (node != 0 && node->type() == Node::Function) {
+ ((FunctionNode *) node)->setOverload(true);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1'")
+ .arg(COMMAND_OVERLOAD));
+ }
+ }
+ else if (command == COMMAND_REIMP) {
+ if (node != 0 && node->type() == Node::Function) {
+ FunctionNode *func = (FunctionNode *) node;
+ const FunctionNode *from = func->reimplementedFrom();
+ if (from == 0) {
+ doc.location().warning(
+ tr("Cannot find base function for '\\%1' in %2()")
+ .arg(COMMAND_REIMP).arg(node->name()),
+ tr("The function either doesn't exist in any base class "
+ "with the same signature or it exists but isn't virtual."));
+ }
+ /*
+ Ideally, we would enable this check to warn whenever
+ \reimp is used incorrectly, and only make the node
+ internal if the function is a reimplementation of
+ another function in a base class.
+ */
+ else if (from->access() == Node::Private
+ || from->parent()->access() == Node::Private) {
+ doc.location().warning(tr("'\\%1' in %2() should be '\\internal' because its base function is private or internal")
+ .arg(COMMAND_REIMP).arg(node->name()));
+ }
+
+ func->setReimp(true);
+ }
+ else {
+ doc.location().warning(tr("Ignored '\\%1' in %2")
+ .arg(COMMAND_REIMP)
+ .arg(node->name()));
+ }
+ }
+ else if (command == COMMAND_RELATES) {
+ InnerNode *pseudoParent;
+ if (arg.startsWith(QLatin1Char('<')) || arg.startsWith('"')) {
+ pseudoParent =
+ static_cast(tre->findNode(QStringList(arg),
+ Node::Fake));
+ }
+ else {
+ QStringList newPath = arg.split("::");
+ pseudoParent =
+ static_cast(tre->findNode(QStringList(newPath),
+ Node::Class));
+ if (!pseudoParent)
+ pseudoParent =
+ static_cast(tre->findNode(QStringList(newPath),
+ Node::Namespace));
+ }
+ if (!pseudoParent) {
+ doc.location().warning(tr("Cannot find '%1' in '\\%2'")
+ .arg(arg).arg(COMMAND_RELATES));
+ }
+ else {
+ node->setRelates(pseudoParent);
+ }
+ }
+ else if (command == COMMAND_CONTENTSPAGE) {
+ setLink(node, Node::ContentsLink, arg);
+ }
+ else if (command == COMMAND_NEXTPAGE) {
+ setLink(node, Node::NextLink, arg);
+ }
+ else if (command == COMMAND_PREVIOUSPAGE) {
+ setLink(node, Node::PreviousLink, arg);
+ }
+ else if (command == COMMAND_INDEXPAGE) {
+ setLink(node, Node::IndexLink, arg);
+ }
+ else if (command == COMMAND_STARTPAGE) {
+ setLink(node, Node::StartLink, arg);
+ }
+ else if (command == COMMAND_QMLINHERITS) {
+ if (node->name() == arg)
+ doc.location().warning(tr("%1 tries to inherit itself").arg(arg));
+ else {
+ setLink(node, Node::InheritsLink, arg);
+ if (node->subType() == Node::QmlClass) {
+ QmlClassNode::addInheritedBy(arg,node);
+ }
+ }
+ }
+ else if (command == COMMAND_QMLDEFAULT) {
+ if (node->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast(node);
+ qpn->setDefault();
+ }
+ else if (node->type() == Node::Fake && node->subType() == Node::QmlPropertyGroup) {
+ QmlPropGroupNode* qpgn = static_cast(node);
+ qpgn->setDefault();
+ }
+ }
+ else if (command == COMMAND_QMLREADONLY) {
+ if (node->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast(node);
+ qpn->setReadOnly(1);
+ }
+ else if (node->type() == Node::Fake && node->subType() == Node::QmlPropertyGroup) {
+ QmlPropGroupNode* qpgn = static_cast(node);
+ qpgn->setReadOnly(1);
+ NodeList::ConstIterator p = qpgn->childNodes().begin();
+ while (p != qpgn->childNodes().end()) {
+ if ((*p)->type() == Node::QmlProperty) {
+ QmlPropertyNode* qpn = static_cast(*p);
+ qpn->setReadOnly(1);
+ }
+ ++p;
+ }
+ }
+ }
+ else if (command == COMMAND_QMLABSTRACT) {
+ if ((node->type() == Node::Fake) && (node->subType() == Node::QmlClass)) {
+ node->setAbstract(true);
+ }
+ }
+ else {
+ processCommonMetaCommand(doc.location(),command,arg,node,tre);
+ }
+}
+
+/*!
+ The topic command has been processed resulting in the \a doc
+ and \a node passed in here. Process the other meta commands,
+ which are found in \a doc, in the context of the topic \a node.
+ */
+void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)
+{
+ const QSet metaCommands = doc.metaCommandsUsed();
+ QSet::ConstIterator cmd = metaCommands.begin();
+ while (cmd != metaCommands.end()) {
+ QStringList args = doc.metaCommandArgs(*cmd);
+ QStringList::ConstIterator arg = args.begin();
+ while (arg != args.end()) {
+ processOtherMetaCommand(doc, *cmd, *arg, node);
+ ++arg;
+ }
+ ++cmd;
+ }
+}
+
+/*!
+ Resets the C++ code parser to its default initialized state.
+ */
+void CppCodeParser::reset(Tree *tree)
+{
+ tre = tree;
+ tokenizer = 0;
+ tok = 0;
+ access = Node::Public;
+ metaness = FunctionNode::Plain;
+ lastPath.clear();
+ moduleName = "";
+}
+
+/*!
+ Get the next token from the file being parsed and store it
+ in the token variable.
+ */
+void CppCodeParser::readToken()
+{
+ tok = tokenizer->getToken();
+}
+
+/*!
+ Return the current location in the file being parsed,
+ i.e. the file name, line number, and column number.
+ */
+const Location& CppCodeParser::location()
+{
+ return tokenizer->location();
+}
+
+/*!
+ Return the previous string read from the file being parsed.
+ */
+QString CppCodeParser::previousLexeme()
+{
+ return tokenizer->previousLexeme();
+}
+
+/*!
+ Return the current string string from the file being parsed.
+ */
+QString CppCodeParser::lexeme()
+{
+ return tokenizer->lexeme();
+}
+
+bool CppCodeParser::match(int target)
+{
+ if (tok == target) {
+ readToken();
+ return true;
+ }
+ else
+ return false;
+}
+
+/*!
+ Skip to \a target. If \a target is found before the end
+ of input, return true. Otherwise return false.
+ */
+bool CppCodeParser::skipTo(int target)
+{
+ while ((tok != Tok_Eoi) && (tok != target))
+ readToken();
+ return (tok == target ? true : false);
+}
+
+/*!
+ If the current token is one of the keyword thingees that
+ are used in Qt, skip over it to the next token and return
+ true. Otherwise just return false without reading the
+ next token.
+ */
+bool CppCodeParser::matchCompat()
+{
+ switch (tok) {
+ case Tok_QT_COMPAT:
+ case Tok_QT_COMPAT_CONSTRUCTOR:
+ case Tok_QT_DEPRECATED:
+ case Tok_QT_MOC_COMPAT:
+ case Tok_QT3_SUPPORT:
+ case Tok_QT3_SUPPORT_CONSTRUCTOR:
+ case Tok_QT3_MOC_SUPPORT:
+ readToken();
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool CppCodeParser::matchModuleQualifier(QString& name)
+{
+ bool matches = (lexeme() == QString('.'));
+ if (matches) {
+ do {
+ name += lexeme();
+ readToken();
+ } while ((tok == Tok_Ident) || (lexeme() == QString('.')));
+ }
+ return matches;
+}
+
+bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
+{
+ bool matches = (tok == Tok_LeftAngle);
+ if (matches) {
+ int leftAngleDepth = 0;
+ int parenAndBraceDepth = 0;
+ do {
+ if (tok == Tok_LeftAngle) {
+ leftAngleDepth++;
+ }
+ else if (tok == Tok_RightAngle) {
+ leftAngleDepth--;
+ }
+ else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) {
+ ++parenAndBraceDepth;
+ }
+ else if (tok == Tok_RightParen || tok == Tok_RightBrace) {
+ if (--parenAndBraceDepth < 0)
+ return false;
+ }
+
+ if (dataType != 0)
+ dataType->append(lexeme());
+ readToken();
+ } while (leftAngleDepth > 0 && tok != Tok_Eoi);
+ }
+ return matches;
+}
+
+bool CppCodeParser::matchTemplateHeader()
+{
+ readToken();
+ return matchTemplateAngles();
+}
+
+bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
+{
+ /*
+ This code is really hard to follow... sorry. The loop is there to match
+ Alpha::Beta::Gamma::...::Omega.
+ */
+ for (;;) {
+ bool virgin = true;
+
+ if (tok != Tok_Ident) {
+ /*
+ There is special processing for 'Foo::operator int()'
+ and such elsewhere. This is the only case where we
+ return something with a trailing gulbrandsen ('Foo::').
+ */
+ if (tok == Tok_operator)
+ return true;
+
+ /*
+ People may write 'const unsigned short' or
+ 'short unsigned const' or any other permutation.
+ */
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+ while (match(Tok_signed) || match(Tok_unsigned) ||
+ match(Tok_short) || match(Tok_long) || match(Tok_int64)) {
+ dataType->append(previousLexeme());
+ virgin = false;
+ }
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_Tilde))
+ dataType->append(previousLexeme());
+ }
+
+ if (virgin) {
+ if (match(Tok_Ident))
+ dataType->append(previousLexeme());
+ else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
+ match(Tok_double) || match(Tok_Ellipsis))
+ dataType->append(previousLexeme());
+ else
+ return false;
+ }
+ else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
+ dataType->append(previousLexeme());
+ }
+
+ matchTemplateAngles(dataType);
+
+ while (match(Tok_const) || match(Tok_volatile))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_Gulbrandsen))
+ dataType->append(previousLexeme());
+ else
+ break;
+ }
+
+ while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) ||
+ match(Tok_Caret))
+ dataType->append(previousLexeme());
+
+ if (match(Tok_LeftParenAster)) {
+ /*
+ A function pointer. This would be rather hard to handle without a
+ tokenizer hack, because a type can be followed with a left parenthesis
+ in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
+ as a single token.
+ */
+ dataType->append(previousLexeme());
+ dataType->appendHotspot();
+ if (var != 0 && match(Tok_Ident))
+ *var = previousLexeme();
+ if (!match(Tok_RightParen) || tok != Tok_LeftParen)
+ return false;
+ dataType->append(previousLexeme());
+
+ int parenDepth0 = tokenizer->parenDepth();
+ while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {
+ dataType->append(lexeme());
+ readToken();
+ }
+ if (match(Tok_RightParen))
+ dataType->append(previousLexeme());
+ }
+ else {
+ /*
+ The common case: Look for an optional identifier, then for
+ some array brackets.
+ */
+ dataType->appendHotspot();
+
+ if (var != 0) {
+ if (match(Tok_Ident)) {
+ *var = previousLexeme();
+ }
+ else if (match(Tok_Comment)) {
+ /*
+ A neat hack: Commented-out parameter names are
+ recognized by qdoc. It's impossible to illustrate
+ here inside a C-style comment, because it requires
+ an asterslash. It's also impossible to illustrate
+ inside a C++-style comment, because the explanation
+ does not fit on one line.
+ */
+ if (varComment.exactMatch(previousLexeme()))
+ *var = varComment.cap(1);
+ }
+ }
+
+ if (tok == Tok_LeftBracket) {
+ int bracketDepth0 = tokenizer->bracketDepth();
+ while ((tokenizer->bracketDepth() >= bracketDepth0 &&
+ tok != Tok_Eoi) ||
+ tok == Tok_RightBracket) {
+ dataType->append(lexeme());
+ readToken();
+ }
+ }
+ }
+ return true;
+}
+
+bool CppCodeParser::matchParameter(FunctionNode *func)
+{
+ CodeChunk dataType;
+ QString name;
+ CodeChunk defaultValue;
+
+ if (!matchDataType(&dataType, &name))
+ return false;
+ match(Tok_Comment);
+ if (match(Tok_Equal)) {
+ int parenDepth0 = tokenizer->parenDepth();
+
+ while (tokenizer->parenDepth() >= parenDepth0 &&
+ (tok != Tok_Comma ||
+ tokenizer->parenDepth() > parenDepth0) &&
+ tok != Tok_Eoi) {
+ defaultValue.append(lexeme());
+ readToken();
+ }
+ }
+ func->addParameter(Parameter(dataType.toString(),
+ "",
+ name,
+ defaultValue.toString())); // ###
+ return true;
+}
+
+bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ const QString &templateStuff,
+ Node::Type type,
+ bool attached)
+{
+ CodeChunk returnType;
+ QStringList parentPath;
+ QString name;
+
+ bool compat = false;
+ if (match(Tok_friend))
+ return false;
+ match(Tok_explicit);
+ if (matchCompat())
+ compat = true;
+ bool sta = false;
+ if (match(Tok_static)) {
+ sta = true;
+ if (matchCompat())
+ compat = true;
+ }
+ FunctionNode::Virtualness vir = FunctionNode::NonVirtual;
+ if (match(Tok_virtual)) {
+ vir = FunctionNode::ImpureVirtual;
+ if (matchCompat())
+ compat = true;
+ }
+
+ if (!matchDataType(&returnType)) {
+ if (tokenizer->parsingFnOrMacro()
+ && (match(Tok_Q_DECLARE_FLAGS) ||
+ match(Tok_Q_PROPERTY) ||
+ match(Tok_Q_PRIVATE_PROPERTY)))
+ returnType = CodeChunk(previousLexeme());
+ else {
+ return false;
+ }
+ }
+
+ if (returnType.toString() == "QBool")
+ returnType = CodeChunk("bool");
+
+ if (matchCompat())
+ compat = true;
+
+ if (tok == Tok_operator &&
+ (returnType.toString().isEmpty() ||
+ returnType.toString().endsWith("::"))) {
+ // 'QString::operator const char *()'
+ parentPath = returnType.toString().split(sep);
+ parentPath.removeAll(QString());
+ returnType = CodeChunk();
+ readToken();
+
+ CodeChunk restOfName;
+ if (tok != Tok_Tilde && matchDataType(&restOfName)) {
+ name = "operator " + restOfName.toString();
+ }
+ else {
+ name = previousLexeme() + lexeme();
+ readToken();
+ while (tok != Tok_LeftParen && tok != Tok_Eoi) {
+ name += lexeme();
+ readToken();
+ }
+ }
+ if (tok != Tok_LeftParen) {
+ return false;
+ }
+ }
+ else if (tok == Tok_LeftParen) {
+ // constructor or destructor
+ parentPath = returnType.toString().split(sep);
+ if (!parentPath.isEmpty()) {
+ name = parentPath.last();
+ parentPath.erase(parentPath.end() - 1);
+ }
+ returnType = CodeChunk();
+ }
+ else {
+ while (match(Tok_Ident)) {
+ name = previousLexeme();
+
+ /*
+ This is a hack to let QML module identifiers through.
+ */
+ matchModuleQualifier(name);
+
+ matchTemplateAngles();
+
+ if (match(Tok_Gulbrandsen))
+ parentPath.append(name);
+ else
+ break;
+ }
+
+ if (tok == Tok_operator) {
+ name = lexeme();
+ readToken();
+ while (tok != Tok_Eoi) {
+ name += lexeme();
+ readToken();
+ if (tok == Tok_LeftParen)
+ break;
+ }
+ }
+ if (parent && (tok == Tok_Semicolon ||
+ tok == Tok_LeftBracket ||
+ tok == Tok_Colon)
+ && access != Node::Private) {
+ if (tok == Tok_LeftBracket) {
+ returnType.appendHotspot();
+
+ int bracketDepth0 = tokenizer->bracketDepth();
+ while ((tokenizer->bracketDepth() >= bracketDepth0 &&
+ tok != Tok_Eoi) ||
+ tok == Tok_RightBracket) {
+ returnType.append(lexeme());
+ readToken();
+ }
+ if (tok != Tok_Semicolon) {
+ return false;
+ }
+ }
+ else if (tok == Tok_Colon) {
+ returnType.appendHotspot();
+
+ while (tok != Tok_Semicolon && tok != Tok_Eoi) {
+ returnType.append(lexeme());
+ readToken();
+ }
+ if (tok != Tok_Semicolon) {
+ return false;
+ }
+ }
+
+ VariableNode *var = new VariableNode(parent, name);
+ var->setAccess(access);
+ var->setLocation(location());
+ var->setLeftType(returnType.left());
+ var->setRightType(returnType.right());
+ if (compat)
+ var->setStatus(Node::Compat);
+ var->setStatic(sta);
+ return false;
+ }
+ if (tok != Tok_LeftParen) {
+ return false;
+ }
+ }
+ readToken();
+
+ FunctionNode *func = new FunctionNode(type, parent, name, attached);
+ func->setAccess(access);
+ func->setLocation(location());
+ func->setReturnType(returnType.toString());
+ func->setParentPath(parentPath);
+ func->setTemplateStuff(templateStuff);
+ if (compat)
+ func->setStatus(Node::Compat);
+
+ func->setMetaness(metaness);
+ if (parent) {
+ if (name == parent->name()) {
+ func->setMetaness(FunctionNode::Ctor);
+ } else if (name.startsWith(QLatin1Char('~'))) {
+ func->setMetaness(FunctionNode::Dtor);
+ }
+ }
+ func->setStatic(sta);
+
+ if (tok != Tok_RightParen) {
+ do {
+ if (!matchParameter(func)) {
+ return false;
+ }
+ } while (match(Tok_Comma));
+ }
+ if (!match(Tok_RightParen)) {
+ return false;
+ }
+
+ func->setConst(match(Tok_const));
+
+ if (match(Tok_Equal) && match(Tok_Number))
+ vir = FunctionNode::PureVirtual;
+ func->setVirtualness(vir);
+
+ if (match(Tok_Colon)) {
+ while (tok != Tok_LeftBrace && tok != Tok_Eoi)
+ readToken();
+ }
+
+ if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
+ int braceDepth0 = tokenizer->braceDepth();
+
+ if (!match(Tok_LeftBrace)) {
+ return false;
+ }
+ while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)
+ readToken();
+ match(Tok_RightBrace);
+ }
+ if (parentPathPtr != 0)
+ *parentPathPtr = parentPath;
+ if (funcPtr != 0)
+ *funcPtr = func;
+ return true;
+}
+
+bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)
+{
+ Node::Access access;
+
+ switch (tok) {
+ case Tok_public:
+ access = Node::Public;
+ readToken();
+ break;
+ case Tok_protected:
+ access = Node::Protected;
+ readToken();
+ break;
+ case Tok_private:
+ access = Node::Private;
+ readToken();
+ break;
+ default:
+ access = isClass ? Node::Private : Node::Public;
+ }
+
+ if (tok == Tok_virtual)
+ readToken();
+
+ CodeChunk baseClass;
+ if (!matchDataType(&baseClass))
+ return false;
+
+ tre->addBaseClass(classe,
+ access,
+ baseClass.toPath(),
+ baseClass.toString(),
+ classe->parent());
+ return true;
+}
+
+bool CppCodeParser::matchBaseList(ClassNode *classe, bool isClass)
+{
+ for (;;) {
+ if (!matchBaseSpecifier(classe, isClass))
+ return false;
+ if (tok == Tok_LeftBrace)
+ return true;
+ if (!match(Tok_Comma))
+ return false;
+ }
+}
+
+/*!
+ Parse a C++ class, union, or struct declarion.
+ */
+bool CppCodeParser::matchClassDecl(InnerNode *parent,
+ const QString &templateStuff)
+{
+ bool isClass = (tok == Tok_class);
+ readToken();
+
+ bool compat = matchCompat();
+
+ if (tok != Tok_Ident)
+ return false;
+ while (tok == Tok_Ident)
+ readToken();
+ if (tok != Tok_Colon && tok != Tok_LeftBrace)
+ return false;
+
+ /*
+ So far, so good. We have 'class Foo {' or 'class Foo :'.
+ This is enough to recognize a class definition.
+ */
+ ClassNode *classe = new ClassNode(parent, previousLexeme());
+ classe->setAccess(access);
+ classe->setLocation(location());
+ if (compat)
+ classe->setStatus(Node::Compat);
+ if (!moduleName.isEmpty())
+ classe->setModuleName(moduleName);
+ classe->setTemplateStuff(templateStuff);
+
+ if (match(Tok_Colon) && !matchBaseList(classe, isClass))
+ return false;
+ if (!match(Tok_LeftBrace))
+ return false;
+
+ Node::Access outerAccess = access;
+ access = isClass ? Node::Private : Node::Public;
+ FunctionNode::Metaness outerMetaness = metaness;
+ metaness = FunctionNode::Plain;
+
+ bool matches = (matchDeclList(classe) && match(Tok_RightBrace) &&
+ match(Tok_Semicolon));
+ access = outerAccess;
+ metaness = outerMetaness;
+ return matches;
+}
+
+bool CppCodeParser::matchNamespaceDecl(InnerNode *parent)
+{
+ readToken(); // skip 'namespace'
+ if (tok != Tok_Ident)
+ return false;
+ while (tok == Tok_Ident)
+ readToken();
+ if (tok != Tok_LeftBrace)
+ return false;
+
+ /*
+ So far, so good. We have 'namespace Foo {'.
+ */
+ QString namespaceName = previousLexeme();
+ NamespaceNode *namespasse = 0;
+ if (parent) {
+ namespasse = static_cast(parent->findNode(namespaceName, Node::Namespace));
+ }
+ if (!namespasse) {
+ namespasse = new NamespaceNode(parent, namespaceName);
+ namespasse->setAccess(access);
+ namespasse->setLocation(location());
+ }
+
+ readToken(); // skip '{'
+ bool matched = matchDeclList(namespasse);
+
+ return matched && match(Tok_RightBrace);
+}
+
+bool CppCodeParser::matchUsingDecl()
+{
+ readToken(); // skip 'using'
+
+ // 'namespace'
+ if (tok != Tok_namespace)
+ return false;
+
+ readToken();
+ // identifier
+ if (tok != Tok_Ident)
+ return false;
+
+ QString name;
+ while (tok == Tok_Ident) {
+ name += lexeme();
+ readToken();
+ if (tok == Tok_Semicolon)
+ break;
+ else if (tok != Tok_Gulbrandsen)
+ return false;
+ name += "::";
+ readToken();
+ }
+
+ /*
+ So far, so good. We have 'using namespace Foo;'.
+ */
+ usedNamespaces.insert(name);
+ return true;
+}
+
+bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume)
+{
+ if (!match(Tok_Ident))
+ return false;
+
+ QString name = previousLexeme();
+ CodeChunk val;
+
+ if (match(Tok_Equal)) {
+ while (tok != Tok_Comma && tok != Tok_RightBrace &&
+ tok != Tok_Eoi) {
+ val.append(lexeme());
+ readToken();
+ }
+ }
+
+ if (enume) {
+ QString strVal = val.toString();
+ if (strVal.isEmpty()) {
+ if (enume->items().isEmpty()) {
+ strVal = "0";
+ }
+ else {
+ QString last = enume->items().last().value();
+ bool ok;
+ int n = last.toInt(&ok);
+ if (ok) {
+ if (last.startsWith(QLatin1Char('0')) && last.size() > 1) {
+ if (last.startsWith("0x") || last.startsWith("0X"))
+ strVal = last.left(2) + QString::number(n + 1, 16);
+ else
+ strVal = QLatin1Char('0') + QString::number(n + 1, 8);
+ }
+ else
+ strVal = QString::number(n + 1);
+ }
+ }
+ }
+
+ enume->addItem(EnumItem(name, strVal));
+ }
+ else {
+ VariableNode *var = new VariableNode(parent, name);
+ var->setAccess(access);
+ var->setLocation(location());
+ var->setLeftType("const int");
+ var->setStatic(true);
+ }
+ return true;
+}
+
+bool CppCodeParser::matchEnumDecl(InnerNode *parent)
+{
+ QString name;
+
+ if (!match(Tok_enum))
+ return false;
+ if (match(Tok_Ident))
+ name = previousLexeme();
+ if (tok != Tok_LeftBrace)
+ return false;
+
+ EnumNode *enume = 0;
+
+ if (!name.isEmpty()) {
+ enume = new EnumNode(parent, name);
+ enume->setAccess(access);
+ enume->setLocation(location());
+ }
+
+ readToken();
+
+ if (!matchEnumItem(parent, enume))
+ return false;
+
+ while (match(Tok_Comma)) {
+ if (!matchEnumItem(parent, enume))
+ return false;
+ }
+ return match(Tok_RightBrace) && match(Tok_Semicolon);
+}
+
+bool CppCodeParser::matchTypedefDecl(InnerNode *parent)
+{
+ CodeChunk dataType;
+ QString name;
+
+ if (!match(Tok_typedef))
+ return false;
+ if (!matchDataType(&dataType, &name))
+ return false;
+ if (!match(Tok_Semicolon))
+ return false;
+
+ if (parent && !parent->findNode(name, Node::Typedef)) {
+ TypedefNode *typedeffe = new TypedefNode(parent, name);
+ typedeffe->setAccess(access);
+ typedeffe->setLocation(location());
+ }
+ return true;
+}
+
+bool CppCodeParser::matchProperty(InnerNode *parent)
+{
+ int expected_tok = Tok_LeftParen;
+ if (match(Tok_Q_PRIVATE_PROPERTY)) {
+ expected_tok = Tok_Comma;
+ if (!skipTo(Tok_Comma))
+ return false;
+ }
+ else if (!match(Tok_Q_PROPERTY) &&
+ !match(Tok_Q_OVERRIDE) &&
+ !match(Tok_QDOC_PROPERTY)) {
+ return false;
+ }
+
+ if (!match(expected_tok))
+ return false;
+
+ QString name;
+ CodeChunk dataType;
+ if (!matchDataType(&dataType, &name))
+ return false;
+
+ PropertyNode *property = new PropertyNode(parent, name);
+ property->setAccess(Node::Public);
+ property->setLocation(location());
+ property->setDataType(dataType.toString());
+
+ while (tok != Tok_RightParen && tok != Tok_Eoi) {
+ if (!match(Tok_Ident))
+ return false;
+ QString key = previousLexeme();
+ QString value;
+
+ if (match(Tok_Ident) || match(Tok_Number)) {
+ value = previousLexeme();
+ }
+ else if (match(Tok_LeftParen)) {
+ int depth = 1;
+ while (tok != Tok_Eoi) {
+ if (tok == Tok_LeftParen) {
+ readToken();
+ ++depth;
+ } else if (tok == Tok_RightParen) {
+ readToken();
+ if (--depth == 0)
+ break;
+ } else {
+ readToken();
+ }
+ }
+ value = "?";
+ }
+
+ if (key == "READ")
+ tre->addPropertyFunction(property, value, PropertyNode::Getter);
+ else if (key == "WRITE") {
+ tre->addPropertyFunction(property, value, PropertyNode::Setter);
+ property->setWritable(true);
+ }
+ else if (key == "STORED")
+ property->setStored(value.toLower() == "true");
+ else if (key == "DESIGNABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setDesignable(true);
+ else if (v == "false")
+ property->setDesignable(false);
+ else {
+ property->setDesignable(false);
+ property->setRuntimeDesFunc(value);
+ }
+ }
+ else if (key == "RESET")
+ tre->addPropertyFunction(property, value, PropertyNode::Resetter);
+ else if (key == "NOTIFY") {
+ tre->addPropertyFunction(property, value, PropertyNode::Notifier);
+ } else if (key == "REVISION") {
+ int revision;
+ bool ok;
+ revision = value.toInt(&ok);
+ if (ok)
+ property->setRevision(revision);
+ else
+ parent->doc().location().warning(tr("Invalid revision number: %1").arg(value));
+ } else if (key == "SCRIPTABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setScriptable(true);
+ else if (v == "false")
+ property->setScriptable(false);
+ else {
+ property->setScriptable(false);
+ property->setRuntimeScrFunc(value);
+ }
+ }
+ else if (key == "CONSTANT")
+ property->setConstant();
+ else if (key == "FINAL")
+ property->setFinal();
+ }
+ match(Tok_RightParen);
+ return true;
+}
+
+/*!
+ Parse a C++ declaration.
+ */
+bool CppCodeParser::matchDeclList(InnerNode *parent)
+{
+ QString templateStuff;
+ int braceDepth0 = tokenizer->braceDepth();
+ if (tok == Tok_RightBrace) // prevents failure on empty body
+ braceDepth0++;
+
+ while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi) {
+ switch (tok) {
+ case Tok_Colon:
+ readToken();
+ break;
+ case Tok_class:
+ case Tok_struct:
+ case Tok_union:
+ matchClassDecl(parent, templateStuff);
+ break;
+ case Tok_namespace:
+ matchNamespaceDecl(parent);
+ break;
+ case Tok_using:
+ matchUsingDecl();
+ break;
+ case Tok_template:
+ templateStuff = matchTemplateHeader();
+ continue;
+ case Tok_enum:
+ matchEnumDecl(parent);
+ break;
+ case Tok_typedef:
+ matchTypedefDecl(parent);
+ break;
+ case Tok_private:
+ readToken();
+ access = Node::Private;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_protected:
+ readToken();
+ access = Node::Protected;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_public:
+ readToken();
+ access = Node::Public;
+ metaness = FunctionNode::Plain;
+ break;
+ case Tok_signals:
+ case Tok_Q_SIGNALS:
+ readToken();
+ access = Node::Public;
+ metaness = FunctionNode::Signal;
+ break;
+ case Tok_slots:
+ case Tok_Q_SLOTS:
+ readToken();
+ metaness = FunctionNode::Slot;
+ break;
+ case Tok_Q_OBJECT:
+ readToken();
+ break;
+ case Tok_Q_OVERRIDE:
+ case Tok_Q_PROPERTY:
+ case Tok_Q_PRIVATE_PROPERTY:
+ case Tok_QDOC_PROPERTY:
+ matchProperty(parent);
+ break;
+ case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ sequentialIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ mutableSequentialIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ associativeIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ mutableAssociativeIteratorClasses.insert(previousLexeme(),
+ location().fileName());
+ match(Tok_RightParen);
+ break;
+ case Tok_Q_DECLARE_FLAGS:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident)) {
+ QString flagsType = previousLexeme();
+ if (match(Tok_Comma) && match(Tok_Ident)) {
+ QString enumType = previousLexeme();
+ TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
+ flagsNode->setAccess(access);
+ flagsNode->setLocation(location());
+ EnumNode *enumNode =
+ static_cast(parent->findNode(enumType,
+ Node::Enum));
+ if (enumNode)
+ enumNode->setFlagsType(flagsNode);
+ }
+ }
+ match(Tok_RightParen);
+ break;
+ case Tok_QT_MODULE:
+ readToken();
+ if (match(Tok_LeftParen) && match(Tok_Ident))
+ moduleName = previousLexeme();
+ if (!moduleName.startsWith("Qt"))
+ moduleName.prepend("Qt");
+ match(Tok_RightParen);
+ break;
+ default:
+ if (!matchFunctionDecl(parent, 0, 0, templateStuff)) {
+ while (tok != Tok_Eoi &&
+ (tokenizer->braceDepth() > braceDepth0 ||
+ (!match(Tok_Semicolon) &&
+ tok != Tok_public && tok != Tok_protected &&
+ tok != Tok_private)))
+ readToken();
+ }
+ }
+ templateStuff.clear();
+ }
+ return true;
+}
+
+/*!
+ This is called by parseSourceFile() to do the actual parsing
+ and tree building.
+ */
+bool CppCodeParser::matchDocsAndStuff()
+{
+ QSet topicCommandsAllowed = topicCommands();
+ QSet otherMetacommandsAllowed = otherMetaCommands();
+ QSet metacommandsAllowed = topicCommandsAllowed +
+ otherMetacommandsAllowed;
+
+ while (tok != Tok_Eoi) {
+ if (tok == Tok_Doc) {
+ /*
+ lexeme() returns an entire qdoc comment.
+ */
+ QString comment = lexeme();
+ Location start_loc(location());
+ readToken();
+
+ Doc::trimCStyleComment(start_loc,comment);
+ Location end_loc(location());
+
+ /*
+ Doc parses the comment.
+ */
+ Doc doc(start_loc,end_loc,comment,metacommandsAllowed);
+
+ QString topic;
+ QStringList args;
+
+ QSet topicCommandsUsed = topicCommandsAllowed &
+ doc.metaCommandsUsed();
+
+ /*
+ There should be one topic command in the set,
+ or none. If the set is empty, then the comment
+ should be a function description.
+ */
+ if (topicCommandsUsed.count() > 0) {
+ topic = *topicCommandsUsed.begin();
+ args = doc.metaCommandArgs(topic);
+ }
+
+ NodeList nodes;
+ QList docs;
+
+ if (topic.isEmpty()) {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *func = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone)) {
+ foreach (const QString &usedNamespace, usedNamespaces) {
+ QStringList newPath = usedNamespace.split("::") + parentPath;
+ func = tre->findFunctionNode(newPath, clone);
+ if (func)
+ break;
+ }
+ if (func == 0)
+ func = tre->findFunctionNode(parentPath, clone);
+
+ if (func) {
+ func->borrowParameterNames(clone);
+ nodes.append(func);
+ docs.append(doc);
+ }
+ delete clone;
+ }
+ else {
+ doc.location().warning(
+ tr("Cannot tie this documentation to anything"),
+ tr("I found a /*! ... */ comment, but there was no "
+ "topic command (e.g., '\\%1', '\\%2') in the "
+ "comment and no function definition following "
+ "the comment.")
+ .arg(COMMAND_FN).arg(COMMAND_PAGE));
+ }
+ }
+ else {
+ /*
+ There is a topic command. Process it.
+ */
+ if ((topic == COMMAND_QMLPROPERTY) ||
+ (topic == COMMAND_QMLATTACHEDPROPERTY)) {
+ Doc nodeDoc = doc;
+ Node *node = processTopicCommandGroup(nodeDoc,topic,args);
+ if (node != 0) {
+ nodes.append(node);
+ docs.append(nodeDoc);
+ }
+ }
+ else {
+ QStringList::ConstIterator a = args.begin();
+ while (a != args.end()) {
+ Doc nodeDoc = doc;
+ Node *node = processTopicCommand(nodeDoc,topic,*a);
+ if (node != 0) {
+ nodes.append(node);
+ docs.append(nodeDoc);
+ }
+ ++a;
+ }
+ }
+ }
+
+ NodeList::Iterator n = nodes.begin();
+ QList::Iterator d = docs.begin();
+ while (n != nodes.end()) {
+ processOtherMetaCommands(*d, *n);
+ (*n)->setDoc(*d);
+ if ((*n)->isInnerNode() &&
+ ((InnerNode *)*n)->includes().isEmpty()) {
+ InnerNode *m = static_cast(*n);
+ while (m->parent() != tre->root())
+ m = m->parent();
+ if (m == *n)
+ ((InnerNode *)*n)->addInclude((*n)->name());
+ else
+ ((InnerNode *)*n)->setIncludes(m->includes());
+ }
+ ++d;
+ ++n;
+ }
+ }
+ else if (tok == Tok_using) {
+ matchUsingDecl();
+ }
+ else {
+ QStringList parentPath;
+ FunctionNode *clone;
+ FunctionNode *node = 0;
+
+ if (matchFunctionDecl(0, &parentPath, &clone)) {
+ /*
+ The location of the definition is more interesting
+ than that of the declaration. People equipped with
+ a sophisticated text editor can respond to warnings
+ concerning undocumented functions very quickly.
+
+ Signals are implemented in uninteresting files
+ generated by moc.
+ */
+ node = tre->findFunctionNode(parentPath, clone);
+ if (node != 0 && node->metaness() != FunctionNode::Signal)
+ node->setLocation(clone->location());
+ delete clone;
+ }
+ else {
+ if (tok != Tok_Doc)
+ readToken();
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ This function uses a Tokenizer to parse the function \a signature
+ in an attempt to match it to the signature of a child node of \a root.
+ If a match is found, \a funcPtr is set to point to the matching node
+ and true is returned.
+ */
+bool CppCodeParser::makeFunctionNode(const QString& signature,
+ QStringList* parentPathPtr,
+ FunctionNode** funcPtr,
+ InnerNode* root,
+ Node::Type type,
+ bool attached)
+{
+ Tokenizer* outerTokenizer = tokenizer;
+ int outerTok = tok;
+
+ Location loc;
+ QByteArray latin1 = signature.toLatin1();
+ Tokenizer stringTokenizer(loc, latin1);
+ stringTokenizer.setParsingFnOrMacro(true);
+ tokenizer = &stringTokenizer;
+ readToken();
+
+ bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr, QString(), type, attached);
+ // potential memory leak with funcPtr
+
+ tokenizer = outerTokenizer;
+ tok = outerTok;
+ return ok;
+}
+
+/*!
+ Create a new FunctionNode for a QML method or signal, as
+ specified by \a type, as a child of \a parent. \a sig is
+ the complete signature, and if \a attached is true, the
+ method or signal is "attached". \a qdoctag is the text of
+ the \a type.
+
+ \a parent is the QML class node. The QML module and QML
+ element names have already been consumed to find \a parent.
+ What remains in \a sig is the method signature. The method
+ must be a child of \a parent.
+ */
+FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
+ const QString& sig,
+ InnerNode* parent,
+ Node::Type type,
+ bool attached,
+ QString qdoctag)
+{
+ QStringList pp;
+ FunctionNode* fn = 0;
+ if (!makeFunctionNode(sig,&pp,&fn,parent,type,attached) &&
+ !makeFunctionNode("void "+sig,&pp,&fn,parent,type,attached)) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag));
+ }
+ return fn;
+}
+
+void CppCodeParser::parseQiteratorDotH(const Location &location,
+ const QString &filePath)
+{
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly))
+ return;
+
+ QString text = file.readAll();
+ text.remove("\r");
+ text.remove("\\\n");
+ QStringList lines = text.split(QLatin1Char('\n'));
+ lines = lines.filter("Q_DECLARE");
+ lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), "");
+
+ if (lines.size() == 4) {
+ sequentialIteratorDefinition = lines[0];
+ mutableSequentialIteratorDefinition = lines[1];
+ associativeIteratorDefinition = lines[2];
+ mutableAssociativeIteratorDefinition = lines[3];
+ }
+ else {
+ location.warning(tr("The qiterator.h hack failed"));
+ }
+}
+
+void CppCodeParser::instantiateIteratorMacro(const QString &container,
+ const QString &includeFile,
+ const QString ¯oDef,
+ Tree * /* tree */)
+{
+ QString resultingCode = macroDef;
+ resultingCode.replace(QRegExp("\\bC\\b"), container);
+ resultingCode.remove(QRegExp("\\s*##\\s*"));
+
+ Location loc(includeFile); // hack to get the include file for free
+ QByteArray latin1 = resultingCode.toLatin1();
+ Tokenizer stringTokenizer(loc, latin1);
+ tokenizer = &stringTokenizer;
+ readToken();
+ matchDeclList(tre->root());
+}
+
+void CppCodeParser::createExampleFileNodes(FakeNode *fake)
+{
+ QString examplePath = fake->name();
+ QString proFileName = examplePath + QLatin1Char('/') + examplePath.split(QLatin1Char('/')).last() + ".pro";
+ QString userFriendlyFilePath;
+
+ QString fullPath = Config::findFile(fake->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+
+ if (fullPath.isEmpty()) {
+ QString tmp = proFileName;
+ proFileName = examplePath + QLatin1Char('/') + "qbuild.pro";
+ userFriendlyFilePath.clear();
+ fullPath = Config::findFile(fake->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ proFileName = examplePath + QLatin1Char('/') + examplePath.split(QLatin1Char('/')).last() + ".qmlproject";
+ userFriendlyFilePath.clear();
+ fullPath = Config::findFile(fake->doc().location(),
+ exampleFiles,
+ exampleDirs,
+ proFileName,
+ userFriendlyFilePath);
+ if (fullPath.isEmpty()) {
+ fake->doc().location().warning(tr("Cannot find file '%1' or '%2'").arg(tmp).arg(proFileName));
+ fake->doc().location().warning(tr("EXAMPLE PATH DOES NOT EXIST: %1").arg(examplePath));
+ return;
+ }
+ }
+ }
+
+ int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();
+ fullPath.truncate(fullPath.lastIndexOf('/'));
+
+ QStringList exampleFiles = Config::getFilesHere(fullPath,exampleNameFilter);
+ QString imagesPath = fullPath + "/images";
+ QStringList imageFiles = Config::getFilesHere(imagesPath,exampleImageFilter);
+ if (!exampleFiles.isEmpty()) {
+ // move main.cpp and to the end, if it exists
+ QString mainCpp;
+ QMutableStringListIterator i(exampleFiles);
+ i.toBack();
+ while (i.hasPrevious()) {
+ QString fileName = i.previous();
+ if (fileName.endsWith("/main.cpp")) {
+ mainCpp = fileName;
+ i.remove();
+ }
+ else if (fileName.contains("/qrc_") || fileName.contains("/moc_")
+ || fileName.contains("/ui_"))
+ i.remove();
+ }
+ if (!mainCpp.isEmpty())
+ exampleFiles.append(mainCpp);
+
+ // add any qmake Qt resource files and qmake project files
+ exampleFiles += Config::getFilesHere(fullPath, "*.qrc *.pro *.qmlproject qmldir");
+ }
+
+ foreach (const QString &exampleFile, exampleFiles)
+ (void) new FakeNode(fake,
+ exampleFile.mid(sizeOfBoringPartOfName),
+ Node::File,
+ Node::NoPageType);
+ foreach (const QString &imageFile, imageFiles) {
+ new FakeNode(fake,
+ imageFile.mid(sizeOfBoringPartOfName),
+ Node::Image,
+ Node::NoPageType);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/tools/qdoc/cppcodeparser.h b/src/tools/qdoc/cppcodeparser.h
new file mode 100644
index 0000000..f33ff64
--- /dev/null
+++ b/src/tools/qdoc/cppcodeparser.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ cppcodeparser.h
+*/
+
+#ifndef CPPCODEPARSER_H
+#define CPPCODEPARSER_H
+
+#include
+
+#include "codeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class CodeChunk;
+class CppCodeParserPrivate;
+class FunctionNode;
+class InnerNode;
+class Tokenizer;
+
+class CppCodeParser : public CodeParser
+{
+public:
+ CppCodeParser();
+ ~CppCodeParser();
+
+ virtual void initializeParser(const Config& config);
+ virtual void terminateParser();
+ virtual QString language();
+ virtual QStringList headerFileNameFilter();
+ virtual QStringList sourceFileNameFilter();
+ virtual void parseHeaderFile(const Location& location,
+ const QString& filePath,
+ Tree *tree);
+ virtual void parseSourceFile(const Location& location,
+ const QString& filePath,
+ Tree *tree);
+ virtual void doneParsingHeaderFiles(Tree *tree);
+ virtual void doneParsingSourceFiles(Tree *tree);
+
+ const FunctionNode *findFunctionNode(const QString& synopsis,
+ Tree *tree,
+ Node *relative = 0,
+ bool fuzzy = false);
+
+protected:
+ virtual QSet topicCommands();
+ virtual Node *processTopicCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg);
+#ifdef QDOC_QML
+ // might need to implement this in QsCodeParser as well.
+ virtual Node *processTopicCommandGroup(const Doc& doc,
+ const QString& command,
+ const QStringList& args);
+ bool splitQmlPropertyArg(const Doc& doc,
+ const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element,
+ QString& name);
+ bool splitQmlMethodArg(const Doc& doc,
+ const QString& arg,
+ QString& type,
+ QString& module,
+ QString& element);
+#endif
+ virtual QSet otherMetaCommands();
+ virtual void processOtherMetaCommand(const Doc& doc,
+ const QString& command,
+ const QString& arg,
+ Node *node);
+ void processOtherMetaCommands(const Doc& doc, Node *node);
+
+private:
+ void reset(Tree *tree);
+ void readToken();
+ const Location& location();
+ QString previousLexeme();
+ QString lexeme();
+ bool match(int target);
+ bool skipTo(int target);
+ bool matchCompat();
+ bool matchModuleQualifier(QString& name);
+ bool matchTemplateAngles(CodeChunk *type = 0);
+ bool matchTemplateHeader();
+ bool matchDataType(CodeChunk *type, QString *var = 0);
+ bool matchParameter(FunctionNode *func);
+ bool matchFunctionDecl(InnerNode *parent,
+ QStringList *parentPathPtr = 0,
+ FunctionNode **funcPtr = 0,
+ const QString &templateStuff = QString(),
+ Node::Type type = Node::Function,
+ bool attached = false);
+ bool matchBaseSpecifier(ClassNode *classe, bool isClass);
+ bool matchBaseList(ClassNode *classe, bool isClass);
+ bool matchClassDecl(InnerNode *parent,
+ const QString &templateStuff = QString());
+ bool matchNamespaceDecl(InnerNode *parent);
+ bool matchUsingDecl();
+ bool matchEnumItem(InnerNode *parent, EnumNode *enume);
+ bool matchEnumDecl(InnerNode *parent);
+ bool matchTypedefDecl(InnerNode *parent);
+ bool matchProperty(InnerNode *parent);
+ bool matchDeclList(InnerNode *parent);
+ bool matchDocsAndStuff();
+ bool makeFunctionNode(const QString &synopsis,
+ QStringList *parentPathPtr,
+ FunctionNode **funcPtr,
+ InnerNode *root = 0,
+ Node::Type type = Node::Function,
+ bool attached = false);
+ FunctionNode* makeFunctionNode(const Doc& doc,
+ const QString& sig,
+ InnerNode* parent,
+ Node::Type type,
+ bool attached,
+ QString qdoctag);
+ void parseQiteratorDotH(const Location &location, const QString &filePath);
+ void instantiateIteratorMacro(const QString &container,
+ const QString &includeFile,
+ const QString ¯oDef,
+ Tree *tree);
+ void createExampleFileNodes(FakeNode *fake);
+
+ QMap nodeTypeMap;
+ Tree *tre;
+ Tokenizer *tokenizer;
+ int tok;
+ Node::Access access;
+ FunctionNode::Metaness metaness;
+ QString moduleName;
+ QStringList lastPath;
+ QRegExp varComment;
+ QRegExp sep;
+
+ QString sequentialIteratorDefinition;
+ QString mutableSequentialIteratorDefinition;
+ QString associativeIteratorDefinition;
+ QString mutableAssociativeIteratorDefinition;
+ QSet usedNamespaces;
+ QMap sequentialIteratorClasses;
+ QMap mutableSequentialIteratorClasses;
+ QMap associativeIteratorClasses;
+ QMap mutableAssociativeIteratorClasses;
+
+ static QStringList exampleFiles;
+ static QStringList exampleDirs;
+ QString exampleNameFilter;
+ QString exampleImageFilter;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp
new file mode 100644
index 0000000..a4c5cbe
--- /dev/null
+++ b/src/tools/qdoc/ditaxmlgenerator.cpp
@@ -0,0 +1,6434 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ ditaxmlgenerator.cpp
+*/
+
+#include "codemarker.h"
+#include "codeparser.h"
+#include "ditaxmlgenerator.h"
+#include "node.h"
+#include "quoter.h"
+#include "separator.h"
+#include "tree.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_VERSION Doc::alias("version")
+int DitaXmlGenerator::id = 0;
+
+/*
+ The strings in this array must appear in the same order as
+ the values in enum DitaXmlGenerator::DitaTag.
+ */
+QString DitaXmlGenerator::ditaTags[] =
+{
+ "",
+ "alt",
+ "apiDesc",
+ "APIMap",
+ "apiName",
+ "apiRelation",
+ "audience",
+ "author",
+ "b",
+ "body",
+ "bodydiv",
+ "brand",
+ "category",
+ "codeblock",
+ "comment",
+ "component",
+ "copyrholder",
+ "copyright",
+ "copyryear",
+ "created",
+ "critdates",
+ "cxxAPIMap",
+ "cxxClass",
+ "cxxClassAbstract",
+ "cxxClassAccessSpecifier",
+ "cxxClassAPIItemLocation",
+ "cxxClassBaseClass",
+ "cxxClassDeclarationFile",
+ "cxxClassDeclarationFileLine",
+ "cxxClassDeclarationFileLineStart",
+ "cxxClassDeclarationFileLineEnd",
+ "cxxClassDefinition",
+ "cxxClassDerivation",
+ "cxxClassDerivationAccessSpecifier",
+ "cxxClassDerivations",
+ "cxxClassDetail",
+ "cxxClassNested",
+ "cxxClassNestedClass",
+ "cxxClassNestedDetail",
+ "cxxDefine",
+ "cxxDefineAccessSpecifier",
+ "cxxDefineAPIItemLocation",
+ "cxxDefineDeclarationFile",
+ "cxxDefineDeclarationFileLine",
+ "cxxDefineDefinition",
+ "cxxDefineDetail",
+ "cxxDefineNameLookup",
+ "cxxDefineParameter",
+ "cxxDefineParameterDeclarationName",
+ "cxxDefineParameters",
+ "cxxDefinePrototype",
+ "cxxDefineReimplemented",
+ "cxxEnumeration",
+ "cxxEnumerationAccessSpecifier",
+ "cxxEnumerationAPIItemLocation",
+ "cxxEnumerationDeclarationFile",
+ "cxxEnumerationDeclarationFileLine",
+ "cxxEnumerationDeclarationFileLineStart",
+ "cxxEnumerationDeclarationFileLineEnd",
+ "cxxEnumerationDefinition",
+ "cxxEnumerationDetail",
+ "cxxEnumerationNameLookup",
+ "cxxEnumerationPrototype",
+ "cxxEnumerationScopedName",
+ "cxxEnumerator",
+ "cxxEnumeratorInitialiser",
+ "cxxEnumeratorNameLookup",
+ "cxxEnumeratorPrototype",
+ "cxxEnumerators",
+ "cxxEnumeratorScopedName",
+ "cxxFunction",
+ "cxxFunctionAccessSpecifier",
+ "cxxFunctionAPIItemLocation",
+ "cxxFunctionConst",
+ "cxxFunctionConstructor",
+ "cxxFunctionDeclarationFile",
+ "cxxFunctionDeclarationFileLine",
+ "cxxFunctionDeclaredType",
+ "cxxFunctionDefinition",
+ "cxxFunctionDestructor",
+ "cxxFunctionDetail",
+ "cxxFunctionNameLookup",
+ "cxxFunctionParameter",
+ "cxxFunctionParameterDeclarationName",
+ "cxxFunctionParameterDeclaredType",
+ "cxxFunctionParameterDefaultValue",
+ "cxxFunctionParameters",
+ "cxxFunctionPrototype",
+ "cxxFunctionPureVirtual",
+ "cxxFunctionReimplemented",
+ "cxxFunctionScopedName",
+ "cxxFunctionStorageClassSpecifierStatic",
+ "cxxFunctionVirtual",
+ "cxxTypedef",
+ "cxxTypedefAccessSpecifier",
+ "cxxTypedefAPIItemLocation",
+ "cxxTypedefDeclarationFile",
+ "cxxTypedefDeclarationFileLine",
+ "cxxTypedefDefinition",
+ "cxxTypedefDetail",
+ "cxxTypedefNameLookup",
+ "cxxTypedefScopedName",
+ "cxxVariable",
+ "cxxVariableAccessSpecifier",
+ "cxxVariableAPIItemLocation",
+ "cxxVariableDeclarationFile",
+ "cxxVariableDeclarationFileLine",
+ "cxxVariableDeclaredType",
+ "cxxVariableDefinition",
+ "cxxVariableDetail",
+ "cxxVariableNameLookup",
+ "cxxVariablePrototype",
+ "cxxVariableReimplemented",
+ "cxxVariableScopedName",
+ "cxxVariableStorageClassSpecifierStatic",
+ "data",
+ "data-about",
+ "dd",
+ "dl",
+ "dlentry",
+ "dt",
+ "entry",
+ "fig",
+ "i",
+ "image",
+ "keyword",
+ "keywords",
+ "li",
+ "link",
+ "linktext",
+ "lq",
+ "map",
+ "mapref",
+ "metadata",
+ "note",
+ "ol",
+ "othermeta",
+ "p",
+ "parameter",
+ "permissions",
+ "ph",
+ "platform",
+ "pre",
+ "prodinfo",
+ "prodname",
+ "prolog",
+ "publisher",
+ "related-links",
+ "resourceid",
+ "revised",
+ "row",
+ "section",
+ "sectiondiv",
+ "shortdesc",
+ "simpletable",
+ "source",
+ "stentry",
+ "sthead",
+ "strow",
+ "sub",
+ "sup",
+ "table",
+ "tbody",
+ "tgroup",
+ "thead",
+ "title",
+ "tm",
+ "topic",
+ "topicmeta",
+ "topicref",
+ "tt",
+ "u",
+ "ul",
+ "unknown",
+ "vrm",
+ "vrmlist",
+ "xref",
+ ""
+};
+
+static bool showBrokenLinks = false;
+
+/*!
+ Quick, dirty, and very ugly. Unescape \a text
+ so QXmlStreamWriter::writeCharacters() can put
+ the escapes back in again!
+ */
+void DitaXmlGenerator::writeCharacters(const QString& text)
+{
+ QString t = text;
+ t = t.replace("<","<");
+ t = t.replace(">",">");
+ t = t.replace("&","&");
+ t = t.replace(""","\"");
+ xmlWriter().writeCharacters(t);
+}
+
+/*!
+ Appends an element to the current XML stream
+ with the \a href attribute and the \a text.
+ */
+void DitaXmlGenerator::addLink(const QString& href,
+ const QStringRef& text,
+ DitaTag t)
+{
+ if (!href.isEmpty()) {
+ writeStartTag(t);
+ // formathtml
+ writeHrefAttribute(href);
+ writeCharacters(text.toString());
+ writeEndTag(); //
+ }
+ else {
+ writeCharacters(text.toString());
+ }
+}
+
+/*!
+ Push \a t onto the dita tag stack and write the appropriate
+ start tag to the DITA XML file.
+ */
+void DitaXmlGenerator::writeStartTag(DitaTag t)
+{
+ xmlWriter().writeStartElement(ditaTags[t]);
+ tagStack.push(t);
+}
+
+/*!
+ Pop the current DITA tag off the stack, and write the
+ appropriate end tag to the DITA XML file.
+ */
+void DitaXmlGenerator::writeEndTag(DitaTag t)
+{
+ DitaTag top = tagStack.pop();
+ if (t > DT_NONE && top != t)
+ qDebug() << "Expected:" << t << "ACTUAL:" << top;
+ xmlWriter().writeEndElement();
+}
+
+/*!
+ Return the current DITA element tag, the one
+ on top of the stack.
+ */
+DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
+{
+ return tagStack.top();
+}
+
+/*!
+ Write the start tag \c{}. if \a title is not
+ empty, generate a GUID from it and write the GUID as the
+ value of the \e{id} attribute. Then write \a title as
+ the value of the \e {spectitle} attribute.
+
+ Then if \a outputclass is not empty, write it as the value
+ of the \a outputclass attribute.
+
+ Fiunally, set the section nesting level to 1 and return 1.
+ */
+int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& title)
+{
+ writeStartTag(DT_apiDesc);
+ if (!title.isEmpty()) {
+ writeGuidAttribute(title);
+ xmlWriter().writeAttribute("spectitle",title);
+ }
+ if (!outputclass.isEmpty())
+ xmlWriter().writeAttribute("outputclass",outputclass);
+ sectionNestingLevel = 1;
+ return sectionNestingLevel;
+}
+
+/*!
+ If the section nesting level is 0, output a \c{}
+ element with an \e id attribute generated from \a title and
+ an \e outputclass attribute set to \a outputclass.
+ If \a title is null, no \e id attribute is output.
+ If \a outputclass is empty, no \e outputclass attribute
+ is output.
+
+ Finally, increment the section nesting level and return
+ the new value.
+ */
+int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
+{
+ if (sectionNestingLevel == 0) {
+ writeStartTag(DT_section);
+ if (!title.isEmpty())
+ writeGuidAttribute(title);
+ if (!outputclass.isEmpty())
+ xmlWriter().writeAttribute("outputclass",outputclass);
+ }
+ else if (!title.isEmpty()) {
+ writeStartTag(DT_p);
+ writeGuidAttribute(title);
+ if (!outputclass.isEmpty())
+ xmlWriter().writeAttribute("outputclass",outputclass);
+ writeCharacters(title);
+ writeEndTag(); //
+ }
+ return ++sectionNestingLevel;
+}
+
+/*!
+ If the section nesting level is greater than 0, decrement
+ it. If it becomes 0, output a \c {}. Return the
+ decremented section nesting level.
+ */
+int DitaXmlGenerator::leaveSection()
+{
+ if (sectionNestingLevel > 0) {
+ --sectionNestingLevel;
+ if (sectionNestingLevel == 0)
+ writeEndTag(); // or
+ }
+ return sectionNestingLevel;
+}
+
+/*!
+ The default constructor.
+ */
+DitaXmlGenerator::DitaXmlGenerator()
+ : inContents(false),
+ inDetailedDescription(false),
+ inLegaleseText(false),
+ inLink(false),
+ inObsoleteLink(false),
+ inSectionHeading(false),
+ inTableHeader(false),
+ inTableBody(false),
+ noLinks(false),
+ obsoleteLinks(false),
+ offlineDocs(true),
+ threeColumnEnumValueTable(true),
+ codeIndent(0),
+ numTableRows(0),
+ divNestingLevel(0),
+ sectionNestingLevel(0),
+ tableColumnCount(0),
+ funcLeftParen("\\S(\\()"),
+ tree_(0),
+ nodeTypeMaps(Node::LastType,0),
+ nodeSubtypeMaps(Node::LastSubtype,0),
+ pageTypeMaps(Node::OnBeyondZebra,0)
+{
+ // nothing yet.
+}
+
+/*!
+ The destructor has nothing to do.
+ */
+DitaXmlGenerator::~DitaXmlGenerator()
+{
+ GuidMaps::iterator i = guidMaps.begin();
+ while (i != guidMaps.end()) {
+ delete i.value();
+ ++i;
+ }
+}
+
+/*!
+ A lot of internal structures are initialized.
+ */
+void DitaXmlGenerator::initializeGenerator(const Config &config)
+{
+ Generator::initializeGenerator(config);
+ obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS));
+ setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
+
+ style = config.getString(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_STYLE);
+ postHeader = config.getString(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_POSTHEADER);
+ postPostHeader = config.getString(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_POSTPOSTHEADER);
+ footer = config.getString(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_FOOTER);
+ address = config.getString(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_ADDRESS);
+ pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_GENERATEMACREFS);
+
+ project = config.getString(CONFIG_PROJECT);
+ projectDescription = config.getString(CONFIG_DESCRIPTION);
+ if (projectDescription.isEmpty() && !project.isEmpty())
+ projectDescription = project + " Reference Documentation";
+
+ projectUrl = config.getString(CONFIG_URL);
+
+ outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
+ if (outputEncoding.isEmpty())
+ outputEncoding = QLatin1String("ISO-8859-1");
+ outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
+
+ naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
+ if (naturalLanguage.isEmpty())
+ naturalLanguage = QLatin1String("en");
+
+ config.subVarsAndValues("dita.metadata.default",metadataDefaults);
+ QSet editionNames = config.subVars(CONFIG_EDITION);
+ QSet::ConstIterator edition = editionNames.begin();
+ while (edition != editionNames.end()) {
+ QString editionName = *edition;
+ QStringList editionModules = config.getStringList(CONFIG_EDITION +
+ Config::dot +
+ editionName +
+ Config::dot +
+ "modules");
+ QStringList editionGroups = config.getStringList(CONFIG_EDITION +
+ Config::dot +
+ editionName +
+ Config::dot +
+ "groups");
+
+ if (!editionModules.isEmpty())
+ editionModuleMap[editionName] = editionModules;
+ if (!editionGroups.isEmpty())
+ editionGroupMap[editionName] = editionGroups;
+
+ ++edition;
+ }
+
+ stylesheets = config.getStringList(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_STYLESHEETS);
+ customHeadElements = config.getStringList(DitaXmlGenerator::format() +
+ Config::dot +
+ DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
+ codeIndent = config.getInt(CONFIG_CODEINDENT);
+ version = config.getString(CONFIG_VERSION);
+ vrm = version.split(".");
+}
+
+/*!
+ All this does is call the same function in the base class.
+ */
+void DitaXmlGenerator::terminateGenerator()
+{
+ Generator::terminateGenerator();
+}
+
+/*!
+ Returns "DITAXML".
+ */
+QString DitaXmlGenerator::format()
+{
+ return "DITAXML";
+}
+
+/*!
+ Calls lookupGuid() to get a GUID for \a text, then writes
+ it to the XML stream as an "id" attribute, and returns it.
+ */
+QString DitaXmlGenerator::writeGuidAttribute(QString text)
+{
+ QString guid = lookupGuid(outFileName(),text);
+ xmlWriter().writeAttribute("id",guid);
+ return guid;
+}
+
+
+/*!
+ Write's the GUID for the \a node to the current XML stream
+ as an "id" attribute. If the \a node doesn't yet have a GUID,
+ one is generated.
+ */
+void DitaXmlGenerator::writeGuidAttribute(Node* node)
+{
+ xmlWriter().writeAttribute("id",node->guid());
+}
+
+/*!
+ Looks up \a text in the GUID map. If it finds \a text,
+ it returns the associated GUID. Otherwise it inserts
+ \a text into the map with a new GUID, and it returns
+ the new GUID.
+ */
+QString DitaXmlGenerator::lookupGuid(QString text)
+{
+ QMap::const_iterator i = name2guidMap.find(text);
+ if (i != name2guidMap.end())
+ return i.value();
+#if 0
+ QString t = QUuid::createUuid().toString();
+ QString guid = "id-" + t.mid(1,t.length()-2);
+#endif
+ QString guid = Node::cleanId(text);
+ name2guidMap.insert(text,guid);
+ return guid;
+}
+
+/*!
+ First, look up the GUID map for \a fileName. If there isn't
+ a GUID map for \a fileName, create one and insert it into
+ the map of GUID maps. Then look up \a text in that GUID map.
+ If \a text is found, return the associated GUID. Otherwise,
+ insert \a text into the GUID map with a new GUID, and return
+ the new GUID.
+ */
+QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
+{
+ GuidMap* gm = lookupGuidMap(fileName);
+ GuidMap::const_iterator i = gm->find(text);
+ if (i != gm->end())
+ return i.value();
+#if 0
+ QString t = QUuid::createUuid().toString();
+ QString guid = "id-" + t.mid(1,t.length()-2);
+#endif
+ QString guid = Node::cleanId(text);
+ gm->insert(text,guid);
+ return guid;
+}
+
+/*!
+ Looks up \a fileName in the map of GUID maps. If it finds
+ \a fileName, it returns a pointer to the associated GUID
+ map. Otherwise it creates a new GUID map and inserts it
+ into the map of GUID maps with \a fileName as its key.
+ */
+GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
+{
+ GuidMaps::const_iterator i = guidMaps.find(fileName);
+ if (i != guidMaps.end())
+ return i.value();
+ GuidMap* gm = new GuidMap;
+ guidMaps.insert(fileName,gm);
+ return gm;
+}
+
+/*!
+ This is where the DITA XML files are written.
+ \note The file is created in PageGenerator::generateTree().
+ */
+void DitaXmlGenerator::generateTree(const Tree *tree)
+{
+ tree_ = tree;
+ nonCompatClasses.clear();
+ mainClasses.clear();
+ compatClasses.clear();
+ obsoleteClasses.clear();
+ moduleClassMap.clear();
+ moduleNamespaceMap.clear();
+ funcIndex.clear();
+ legaleseTexts.clear();
+ serviceClasses.clear();
+ qmlClasses.clear();
+ findAllClasses(tree->root());
+ findAllFunctions(tree->root());
+ findAllLegaleseTexts(tree->root());
+ findAllNamespaces(tree->root());
+ findAllSince(tree->root());
+
+ PageGenerator::generateTree(tree);
+ writeDitaMap(tree);
+}
+
+void DitaXmlGenerator::startText(const Node* /* relative */,
+ CodeMarker* /* marker */)
+{
+ inLink = false;
+ inContents = false;
+ inSectionHeading = false;
+ inTableHeader = false;
+ numTableRows = 0;
+ threeColumnEnumValueTable = true;
+ link.clear();
+ sectionNumber.clear();
+}
+
+static int countTableColumns(const Atom* t)
+{
+ int result = 0;
+ if (t->type() == Atom::TableHeaderLeft) {
+ while (t->type() == Atom::TableHeaderLeft) {
+ int count = 0;
+ t = t->next();
+ while (t->type() != Atom::TableHeaderRight) {
+ if (t->type() == Atom::TableItemLeft)
+ ++count;
+ t = t->next();
+ }
+ if (count > result)
+ result = count;
+ t = t->next();
+ }
+ }
+ else if (t->type() == Atom::TableRowLeft) {
+ while (t->type() != Atom::TableRowRight) {
+ if (t->type() == Atom::TableItemLeft)
+ ++result;
+ t = t->next();
+ }
+ }
+ return result;
+}
+
+/*!
+ Generate html from an instance of Atom.
+ */
+int DitaXmlGenerator::generateAtom(const Atom *atom,
+ const Node *relative,
+ CodeMarker *marker)
+{
+ int skipAhead = 0;
+ QString hx, str;
+ static bool in_para = false;
+ QString guid, hc, attr;
+
+ switch (atom->type()) {
+ case Atom::AbstractLeft:
+ break;
+ case Atom::AbstractRight:
+ break;
+ case Atom::AutoLink:
+ if (!noLinks && !inLink && !inContents && !inSectionHeading) {
+ const Node* node = 0;
+ QString link = getLink(atom, relative, marker, &node);
+ if (!link.isEmpty()) {
+ beginLink(link);
+ generateLink(atom, relative, marker);
+ endLink();
+ }
+ else {
+ writeCharacters(protectEnc(atom->string()));
+ }
+ }
+ else {
+ writeCharacters(protectEnc(atom->string()));
+ }
+ break;
+ case Atom::BaseName:
+ break;
+ case Atom::BriefLeft:
+ //if (relative->type() == Node::Fake) {
+ //skipAhead = skipAtoms(atom, Atom::BriefRight);
+ //break;
+ //}
+ if (inSection()) {
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass","brief");
+ }
+ else {
+ noLinks = true;
+ writeStartTag(DT_shortdesc);
+ }
+ if (relative->type() == Node::Property ||
+ relative->type() == Node::Variable) {
+ xmlWriter().writeCharacters("This ");
+ if (relative->type() == Node::Property)
+ xmlWriter().writeCharacters("property");
+ else if (relative->type() == Node::Variable)
+ xmlWriter().writeCharacters("variable");
+ xmlWriter().writeCharacters(" holds ");
+ }
+ if (noLinks) {
+ atom = atom->next();
+ while (atom != 0 && atom->type() != Atom::BriefRight) {
+ if (atom->type() == Atom::String ||
+ atom->type() == Atom::AutoLink)
+ str += atom->string();
+ skipAhead++;
+ atom = atom->next();
+ }
+ str[0] = str[0].toLower();
+ if (str.endsWith(QLatin1Char('.')))
+ str.truncate(str.length() - 1);
+ writeCharacters(str + QLatin1Char('.'));
+ }
+ break;
+ case Atom::BriefRight:
+ // if (relative->type() != Node::Fake)
+ writeEndTag(); // or
+ noLinks = false;
+ break;
+ case Atom::C:
+ writeStartTag(DT_tt);
+ if (inLink) {
+ writeCharacters(protectEnc(plainCode(atom->string())));
+ }
+ else {
+ writeText(atom->string(), marker, relative);
+ }
+ writeEndTag(); // see writeStartElement() above
+ break;
+ case Atom::Code:
+ {
+ writeStartTag(DT_codeblock);
+ xmlWriter().writeAttribute("outputclass","cpp");
+ QString chars = trimmedTrailing(atom->string());
+ writeText(chars, marker, relative);
+ writeEndTag(); //
+ }
+ break;
+ case Atom::Qml:
+ writeStartTag(DT_codeblock);
+ xmlWriter().writeAttribute("outputclass","qml");
+ writeText(trimmedTrailing(atom->string()), marker, relative);
+ writeEndTag(); //
+ break;
+ case Atom::CodeNew:
+ writeStartTag(DT_p);
+ xmlWriter().writeCharacters("you can rewrite it as");
+ writeEndTag(); //
+ writeStartTag(DT_codeblock);
+ writeText(trimmedTrailing(atom->string()), marker, relative);
+ writeEndTag(); //
+ break;
+ case Atom::CodeOld:
+ writeStartTag(DT_p);
+ xmlWriter().writeCharacters("For example, if you have code like");
+ writeEndTag(); //
+ // fallthrough
+ case Atom::CodeBad:
+ writeStartTag(DT_codeblock);
+ writeCharacters(trimmedTrailing(plainCode(atom->string())));
+ writeEndTag(); //
+ break;
+ case Atom::DivLeft:
+ {
+ bool inStartElement = false;
+ attr = atom->string();
+ DitaTag t = currentTag();
+ if ((t == DT_section) || (t == DT_sectiondiv)) {
+ writeStartTag(DT_sectiondiv);
+ divNestingLevel++;
+ inStartElement = true;
+ }
+ else if ((t == DT_body) || (t == DT_bodydiv)) {
+ writeStartTag(DT_bodydiv);
+ divNestingLevel++;
+ inStartElement = true;
+ }
+ if (!attr.isEmpty()) {
+ if (attr.contains('=')) {
+ int index = 0;
+ int from = 0;
+ QString values;
+ while (index >= 0) {
+ index = attr.indexOf('"',from);
+ if (index >= 0) {
+ ++index;
+ from = index;
+ index = attr.indexOf('"',from);
+ if (index > from) {
+ if (!values.isEmpty())
+ values.append(' ');
+ values += attr.mid(from,index-from);
+ from = index+1;
+ }
+ }
+ }
+ attr = values;
+ }
+ }
+ if (inStartElement)
+ xmlWriter().writeAttribute("outputclass", attr);
+ }
+ break;
+ case Atom::DivRight:
+ if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
+ writeEndTag(); // , , or
+ if (divNestingLevel > 0)
+ --divNestingLevel;
+ }
+ break;
+ case Atom::FootnoteLeft:
+ // ### For now
+ if (in_para) {
+ writeEndTag(); //
+ in_para = false;
+ }
+ xmlWriter().writeCharacters("");
+ break;
+ case Atom::FormatElse:
+ case Atom::FormatEndif:
+ case Atom::FormatIf:
+ break;
+ case Atom::FormattingLeft:
+ {
+ DitaTag t = DT_LAST;
+ if (atom->string() == ATOM_FORMATTING_BOLD)
+ t = DT_b;
+ else if (atom->string() == ATOM_FORMATTING_PARAMETER)
+ t = DT_i;
+ else if (atom->string() == ATOM_FORMATTING_ITALIC)
+ t = DT_i;
+ else if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ t = DT_tt;
+ else if (atom->string().startsWith("span ")) {
+ t = DT_keyword;
+ }
+ else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
+ t = DT_u;
+ else if (atom->string() == ATOM_FORMATTING_INDEX)
+ t = DT_comment;
+ else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
+ t = DT_sub;
+ else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
+ t = DT_sup;
+ else
+ qDebug() << "DT_LAST";
+ writeStartTag(t);
+ if (atom->string() == ATOM_FORMATTING_PARAMETER) {
+ if (atom->next() != 0 && atom->next()->type() == Atom::String) {
+ QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
+ if (subscriptRegExp.exactMatch(atom->next()->string())) {
+ xmlWriter().writeCharacters(subscriptRegExp.cap(1));
+ writeStartTag(DT_sub);
+ xmlWriter().writeCharacters(subscriptRegExp.cap(2));
+ writeEndTag(); //
+ skipAhead = 1;
+ }
+ }
+ }
+ else if (t == DT_keyword) {
+ QString attr = atom->string().mid(5);
+ if (!attr.isEmpty()) {
+ if (attr.contains('=')) {
+ int index = 0;
+ int from = 0;
+ QString values;
+ while (index >= 0) {
+ index = attr.indexOf('"',from);
+ if (index >= 0) {
+ ++index;
+ from = index;
+ index = attr.indexOf('"',from);
+ if (index > from) {
+ if (!values.isEmpty())
+ values.append(' ');
+ values += attr.mid(from,index-from);
+ from = index+1;
+ }
+ }
+ }
+ attr = values;
+ }
+ }
+ xmlWriter().writeAttribute("outputclass", attr);
+ }
+ }
+ break;
+ case Atom::FormattingRight:
+ if (atom->string() == ATOM_FORMATTING_LINK) {
+ endLink();
+ }
+ else {
+ writeEndTag(); // ?
+ }
+ break;
+ case Atom::AnnotatedList:
+ {
+ QList values = tree_->groups().values(atom->string());
+ NodeMap nodeMap;
+ for (int i = 0; i < values.size(); ++i) {
+ const Node* n = values.at(i);
+ if ((n->status() != Node::Internal) && (n->access() != Node::Private)) {
+ nodeMap.insert(n->nameForLists(),n);
+ }
+ }
+ generateAnnotatedList(relative, marker, nodeMap);
+ }
+ break;
+ case Atom::GeneratedList:
+ if (atom->string() == "annotatedclasses") {
+ generateAnnotatedList(relative, marker, nonCompatClasses);
+ }
+ else if (atom->string() == "classes") {
+ generateCompactList(relative, marker, nonCompatClasses, true);
+ }
+ else if (atom->string() == "qmlclasses") {
+ generateCompactList(relative, marker, qmlClasses, true);
+ }
+ else if (atom->string().contains("classesbymodule")) {
+ QString arg = atom->string().trimmed();
+ QString moduleName = atom->string().mid(atom->string().indexOf(
+ "classesbymodule") + 15).trimmed();
+ if (moduleClassMap.contains(moduleName))
+ generateAnnotatedList(relative, marker, moduleClassMap[moduleName]);
+ }
+ else if (atom->string().contains("classesbyedition")) {
+
+ QString arg = atom->string().trimmed();
+ QString editionName = atom->string().mid(atom->string().indexOf(
+ "classesbyedition") + 16).trimmed();
+
+ if (editionModuleMap.contains(editionName)) {
+
+ // Add all classes in the modules listed for that edition.
+ NodeMap editionClasses;
+ foreach (const QString &moduleName, editionModuleMap[editionName]) {
+ if (moduleClassMap.contains(moduleName))
+ editionClasses.unite(moduleClassMap[moduleName]);
+ }
+
+ // Add additional groups and remove groups of classes that
+ // should be excluded from the edition.
+
+ QMultiMap groups = tree_->groups();
+ foreach (const QString &groupName, editionGroupMap[editionName]) {
+ QList groupClasses;
+ if (groupName.startsWith(QLatin1Char('-'))) {
+ groupClasses = groups.values(groupName.mid(1));
+ foreach (const Node *node, groupClasses)
+ editionClasses.remove(node->name());
+ }
+ else {
+ groupClasses = groups.values(groupName);
+ foreach (const Node *node, groupClasses)
+ editionClasses.insert(node->name(), node);
+ }
+ }
+ generateAnnotatedList(relative, marker, editionClasses);
+ }
+ }
+ else if (atom->string() == "classhierarchy") {
+ generateClassHierarchy(relative, marker, nonCompatClasses);
+ }
+ else if (atom->string() == "compatclasses") {
+ generateCompactList(relative, marker, compatClasses, false);
+ }
+ else if (atom->string() == "obsoleteclasses") {
+ generateCompactList(relative, marker, obsoleteClasses, false);
+ }
+ else if (atom->string() == "functionindex") {
+ generateFunctionIndex(relative, marker);
+ }
+ else if (atom->string() == "legalese") {
+ generateLegaleseList(relative, marker);
+ }
+ else if (atom->string() == "mainclasses") {
+ generateCompactList(relative, marker, mainClasses, true);
+ }
+ else if (atom->string() == "services") {
+ generateCompactList(relative, marker, serviceClasses, false);
+ }
+ else if (atom->string() == "overviews") {
+ generateOverviewList(relative, marker);
+ }
+ else if (atom->string() == "namespaces") {
+ generateAnnotatedList(relative, marker, namespaceIndex);
+ }
+ else if (atom->string() == "related") {
+ const FakeNode *fake = static_cast(relative);
+ if (fake && !fake->groupMembers().isEmpty()) {
+ NodeMap groupMembersMap;
+ foreach (const Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Fake)
+ groupMembersMap[fullName(node, relative, marker)] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+ }
+ break;
+ case Atom::SinceList:
+ {
+ NewSinceMaps::const_iterator nsmap;
+ nsmap = newSinceMaps.find(atom->string());
+ NewClassMaps::const_iterator ncmap;
+ ncmap = newClassMaps.find(atom->string());
+ NewClassMaps::const_iterator nqcmap;
+ nqcmap = newQmlClassMaps.find(atom->string());
+ if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) {
+ QList sections;
+ QList::ConstIterator s;
+ for (int i=0; itype()) {
+ case Node::Fake:
+ if (node->subType() == Node::QmlClass) {
+ sections[QmlClass].appendMember((Node*)node);
+ }
+ break;
+ case Node::Namespace:
+ sections[Namespace].appendMember((Node*)node);
+ break;
+ case Node::Class:
+ sections[Class].appendMember((Node*)node);
+ break;
+ case Node::Enum:
+ sections[Enum].appendMember((Node*)node);
+ break;
+ case Node::Typedef:
+ sections[Typedef].appendMember((Node*)node);
+ break;
+ case Node::Function: {
+ const FunctionNode* fn = static_cast(node);
+ if (fn->isMacro())
+ sections[Macro].appendMember((Node*)node);
+ else {
+ Node* p = fn->parent();
+ if (p) {
+ if (p->type() == Node::Class)
+ sections[MemberFunction].appendMember((Node*)node);
+ else if (p->type() == Node::Namespace) {
+ if (p->name().isEmpty())
+ sections[GlobalFunction].appendMember((Node*)node);
+ else
+ sections[NamespaceFunction].appendMember((Node*)node);
+ }
+ else
+ sections[GlobalFunction].appendMember((Node*)node);
+ }
+ else
+ sections[GlobalFunction].appendMember((Node*)node);
+ }
+ break;
+ }
+ case Node::Property:
+ sections[Property].appendMember((Node*)node);
+ break;
+ case Node::Variable:
+ sections[Variable].appendMember((Node*)node);
+ break;
+ case Node::QmlProperty:
+ sections[QmlProperty].appendMember((Node*)node);
+ break;
+ case Node::QmlSignal:
+ sections[QmlSignal].appendMember((Node*)node);
+ break;
+ case Node::QmlSignalHandler:
+ sections[QmlSignalHandler].appendMember((Node*)node);
+ break;
+ case Node::QmlMethod:
+ sections[QmlMethod].appendMember((Node*)node);
+ break;
+ default:
+ break;
+ }
+ ++n;
+ }
+
+ /*
+ First generate the table of contents.
+ */
+ writeStartTag(DT_ul);
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (!(*s).members.isEmpty()) {
+ QString li = outFileName() + QLatin1Char('#') + Doc::canonicalTitle((*s).name);
+ writeXrefListItem(li, (*s).name);
+ }
+ ++s;
+ }
+ writeEndTag(); //
+
+ int idx = 0;
+ s = sections.constBegin();
+ while (s != sections.constEnd()) {
+ if (!(*s).members.isEmpty()) {
+ writeStartTag(DT_p);
+ writeGuidAttribute(Doc::canonicalTitle((*s).name));
+ xmlWriter().writeAttribute("outputclass","h3");
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ if (idx == Class)
+ generateCompactList(0, marker, ncmap.value(), false, QString("Q"));
+ else if (idx == QmlClass)
+ generateCompactList(0, marker, nqcmap.value(), false, QString("Q"));
+ else if (idx == MemberFunction) {
+ ParentMaps parentmaps;
+ ParentMaps::iterator pmap;
+ NodeList::const_iterator i = s->members.constBegin();
+ while (i != s->members.constEnd()) {
+ Node* p = (*i)->parent();
+ pmap = parentmaps.find(p);
+ if (pmap == parentmaps.end())
+ pmap = parentmaps.insert(p,NodeMultiMap());
+ pmap->insert((*i)->name(),(*i));
+ ++i;
+ }
+ pmap = parentmaps.begin();
+ while (pmap != parentmaps.end()) {
+ NodeList nlist = pmap->values();
+ writeStartTag(DT_p);
+ xmlWriter().writeCharacters("Class ");
+ writeStartTag(DT_xref);
+ // formathtml
+ xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
+ QStringList pieces = fullName(pmap.key(), 0, marker).split("::");
+ writeCharacters(protectEnc(pieces.last()));
+ writeEndTag(); //
+ xmlWriter().writeCharacters(":");
+ writeEndTag(); //
+
+ generateSection(nlist, 0, marker, CodeMarker::Summary);
+ ++pmap;
+ }
+ }
+ else {
+ generateSection(s->members, 0, marker, CodeMarker::Summary);
+ }
+ }
+ ++idx;
+ ++s;
+ }
+ }
+ }
+ break;
+ case Atom::Image:
+ case Atom::InlineImage:
+ {
+ QString fileName = imageFileName(relative, atom->string());
+ QString text;
+ if (atom->next() != 0)
+ text = atom->next()->string();
+ if (fileName.isEmpty()) {
+ QString images = "images";
+ if (!baseDir().isEmpty())
+ images.prepend("../");
+ if (atom->string()[0] != '/')
+ images.append(QLatin1Char('/'));
+ fileName = images + atom->string();
+ }
+ if (relative && (relative->type() == Node::Fake) &&
+ (relative->subType() == Node::Example)) {
+ const ExampleNode* cen = static_cast(relative);
+ if (cen->imageFileName().isEmpty()) {
+ ExampleNode* en = const_cast(cen);
+ en->setImageFileName(fileName);
+ }
+ }
+
+ if (currentTag() != DT_xref)
+ writeStartTag(DT_fig);
+ writeStartTag(DT_image);
+ writeHrefAttribute(protectEnc(fileName));
+ if (atom->type() == Atom::InlineImage)
+ xmlWriter().writeAttribute("placement","inline");
+ else {
+ xmlWriter().writeAttribute("placement","break");
+ xmlWriter().writeAttribute("align","center");
+ }
+ if (!text.isEmpty()) {
+ writeStartTag(DT_alt);
+ writeCharacters(protectEnc(text));
+ writeEndTag(); //
+ }
+ writeEndTag(); //
+ if (currentTag() != DT_xref)
+ writeEndTag(); //
+ }
+ break;
+ case Atom::ImageText:
+ // nothing
+ break;
+ case Atom::ImportantLeft:
+ writeStartTag(DT_note);
+ xmlWriter().writeAttribute("type","important");
+ break;
+ case Atom::ImportantRight:
+ writeEndTag(); //
+ break;
+ case Atom::NoteLeft:
+ writeStartTag(DT_note);
+ xmlWriter().writeAttribute("type","note");
+ break;
+ case Atom::NoteRight:
+ writeEndTag(); //
+ break;
+ case Atom::LegaleseLeft:
+ inLegaleseText = true;
+ break;
+ case Atom::LegaleseRight:
+ inLegaleseText = false;
+ break;
+ case Atom::LineBreak:
+ //xmlWriter().writeEmptyElement("br");
+ break;
+ case Atom::Link:
+ {
+ const Node *node = 0;
+ QString myLink = getLink(atom, relative, marker, &node);
+ if (myLink.isEmpty()) {
+ relative->doc().location().warning(tr("Can't link to '%1' in %2")
+ .arg(atom->string())
+ .arg(marker->plainFullName(relative)));
+ }
+ else if (!inSectionHeading) {
+ beginLink(myLink);
+ }
+#if 0
+ else {
+ //xmlWriter().writeCharacters(atom->string());
+ //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string();
+ }
+#endif
+ skipAhead = 1;
+ }
+ break;
+ case Atom::GuidLink:
+ {
+ beginLink(atom->string());
+ skipAhead = 1;
+ }
+ break;
+ case Atom::LinkNode:
+ {
+ const Node* node = CodeMarker::nodeForString(atom->string());
+ beginLink(linkForNode(node, relative));
+ skipAhead = 1;
+ }
+ break;
+ case Atom::ListLeft:
+ if (in_para) {
+ writeEndTag(); //
+ in_para = false;
+ }
+ if (atom->string() == ATOM_LIST_BULLET) {
+ writeStartTag(DT_ul);
+ }
+ else if (atom->string() == ATOM_LIST_TAG) {
+ writeStartTag(DT_dl);
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
+ if (threeColumnEnumValueTable) {
+ writeStartTag(DT_simpletable);
+ xmlWriter().writeAttribute("outputclass","valuelist");
+ writeStartTag(DT_sthead);
+ writeStartTag(DT_stentry);
+ xmlWriter().writeCharacters("Constant");
+ writeEndTag(); //
+ writeStartTag(DT_stentry);
+ xmlWriter().writeCharacters("Value");
+ writeEndTag(); //
+ writeStartTag(DT_stentry);
+ xmlWriter().writeCharacters("Description");
+ writeEndTag(); //
+ writeEndTag(); //
+ }
+ else {
+ writeStartTag(DT_simpletable);
+ xmlWriter().writeAttribute("outputclass","valuelist");
+ writeStartTag(DT_sthead);
+ writeStartTag(DT_stentry);
+ xmlWriter().writeCharacters("Constant");
+ writeEndTag(); //
+ writeStartTag(DT_stentry);
+ xmlWriter().writeCharacters("Value");
+ writeEndTag(); //
+ writeEndTag(); //
+ }
+ }
+ else {
+ writeStartTag(DT_ol);
+ if (atom->string() == ATOM_LIST_UPPERALPHA)
+ xmlWriter().writeAttribute("outputclass","upperalpha");
+ else if (atom->string() == ATOM_LIST_LOWERALPHA)
+ xmlWriter().writeAttribute("outputclass","loweralpha");
+ else if (atom->string() == ATOM_LIST_UPPERROMAN)
+ xmlWriter().writeAttribute("outputclass","upperroman");
+ else if (atom->string() == ATOM_LIST_LOWERROMAN)
+ xmlWriter().writeAttribute("outputclass","lowerroman");
+ else // (atom->string() == ATOM_LIST_NUMERIC)
+ xmlWriter().writeAttribute("outputclass","numeric");
+ if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
+ // I don't think this attribute is supported.
+ xmlWriter().writeAttribute("start",atom->next()->string());
+ }
+ }
+ break;
+ case Atom::ListItemNumber:
+ // nothing
+ break;
+ case Atom::ListTagLeft:
+ if (atom->string() == ATOM_LIST_TAG) {
+ writeStartTag(DT_dt);
+ }
+ else { // (atom->string() == ATOM_LIST_VALUE)
+ writeStartTag(DT_strow);
+ writeStartTag(DT_stentry);
+ writeStartTag(DT_tt);
+ writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
+ relative))));
+ writeEndTag(); //
+ writeEndTag(); //
+ writeStartTag(DT_stentry);
+
+ QString itemValue;
+ if (relative->type() == Node::Enum) {
+ const EnumNode *enume = static_cast(relative);
+ itemValue = enume->itemValue(atom->next()->string());
+ }
+
+ if (itemValue.isEmpty())
+ xmlWriter().writeCharacters("?");
+ else {
+ writeStartTag(DT_tt);
+ writeCharacters(protectEnc(itemValue));
+ writeEndTag(); //
+ }
+ skipAhead = 1;
+ }
+ break;
+ case Atom::ListTagRight:
+ if (atom->string() == ATOM_LIST_TAG)
+ writeEndTag(); //
+ break;
+ case Atom::ListItemLeft:
+ if (atom->string() == ATOM_LIST_TAG) {
+ writeStartTag(DT_dd);
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ if (threeColumnEnumValueTable) {
+ writeEndTag(); //
+ writeStartTag(DT_stentry);
+ }
+ }
+ else {
+ writeStartTag(DT_li);
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ break;
+ case Atom::ListItemRight:
+ if (atom->string() == ATOM_LIST_TAG) {
+ writeEndTag(); //
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ writeEndTag(); //
+ writeEndTag(); //
+ }
+ else {
+ writeEndTag(); //
+ }
+ break;
+ case Atom::ListRight:
+ if (atom->string() == ATOM_LIST_BULLET) {
+ writeEndTag(); //
+ }
+ else if (atom->string() == ATOM_LIST_TAG) {
+ writeEndTag(); //
+ }
+ else if (atom->string() == ATOM_LIST_VALUE) {
+ writeEndTag(); //
+ }
+ else {
+ writeEndTag(); //
+ }
+ break;
+ case Atom::Nop:
+ // nothing
+ break;
+ case Atom::ParaLeft:
+ writeStartTag(DT_p);
+ if (inLegaleseText)
+ xmlWriter().writeAttribute("outputclass","legalese");
+ in_para = true;
+ break;
+ case Atom::ParaRight:
+ endLink();
+ if (in_para) {
+ writeEndTag(); //
+ in_para = false;
+ }
+ break;
+ case Atom::QuotationLeft:
+ writeStartTag(DT_lq);
+ break;
+ case Atom::QuotationRight:
+ writeEndTag(); //
+ break;
+ case Atom::RawString:
+ if (atom->string() == " ")
+ break;
+ if (atom->string().startsWith(QLatin1Char('&')))
+ writeCharacters(atom->string());
+ else if (atom->string() == "*") {
+ writeStartTag(DT_sup);
+ writeCharacters("*");
+ writeEndTag(); //
+ }
+ else if (atom->string() == "®") {
+ writeStartTag(DT_tm);
+ xmlWriter().writeAttribute("tmtype","reg");
+ writeEndTag(); //
+ }
+ else {
+ writeStartTag(DT_pre);
+ xmlWriter().writeAttribute("outputclass","raw-html");
+ writeCharacters(atom->string());
+ writeEndTag(); //
+ }
+ break;
+ case Atom::SectionLeft:
+#if 0
+ if (inApiDesc) {
+ writeEndTag(); //
+ inApiDesc = false;
+ }
+#endif
+ enterSection("details",QString());
+ //writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
+ break;
+ case Atom::SectionRight:
+ leaveSection();
+ break;
+ case Atom::SectionHeadingLeft:
+ {
+ writeStartTag(DT_p);
+ QString id = Text::sectionHeading(atom).toString();
+ id = stripMarkup(id);
+ id = Doc::canonicalTitle(id);
+ writeGuidAttribute(id);
+ hx = QLatin1Char('h') + QString::number(atom->string().toInt() + hOffset(relative));
+ xmlWriter().writeAttribute("outputclass",hx);
+ inSectionHeading = true;
+ }
+ break;
+ case Atom::SectionHeadingRight:
+ writeEndTag(); // (see case Atom::SectionHeadingLeft)
+ inSectionHeading = false;
+ break;
+ case Atom::SidebarLeft:
+ // nothing
+ break;
+ case Atom::SidebarRight:
+ // nothing
+ break;
+ case Atom::String:
+ if (inLink && !inContents && !inSectionHeading) {
+ generateLink(atom, relative, marker);
+ }
+ else {
+ writeCharacters(atom->string());
+ }
+ break;
+ case Atom::TableLeft:
+ {
+ QString attr;
+ if ((atom->count() > 0) && (atom->string(0) == "borderless"))
+ attr = "borderless";
+ else if ((atom->count() > 1) && (atom->string(1) == "borderless"))
+ attr = "borderless";
+ if (in_para) {
+ writeEndTag(); //
+ in_para = false;
+ }
+ writeStartTag(DT_table);
+ if (!attr.isEmpty())
+ xmlWriter().writeAttribute("outputclass",attr);
+ numTableRows = 0;
+ if (tableColumnCount != 0) {
+ qDebug() << "ERROR: Nested tables!";
+ tableColumnCount = 0;
+ }
+ tableColumnCount = countTableColumns(atom->next());
+ writeStartTag(DT_tgroup);
+ xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
+ inTableHeader = false;
+ inTableBody = false;
+ }
+ break;
+ case Atom::TableRight:
+ writeEndTag(); //
+ writeEndTag(); //
+ writeEndTag(); //
+ inTableHeader = false;
+ inTableBody = false;
+ tableColumnCount = 0;
+ break;
+ case Atom::TableHeaderLeft:
+ if (inTableBody) {
+ writeEndTag(); //
+ writeEndTag(); //
+ writeEndTag(); //
+ inTableHeader = false;
+ inTableBody = false;
+ tableColumnCount = 0;
+ writeStartTag(DT_table);
+ numTableRows = 0;
+ tableColumnCount = countTableColumns(atom);
+ writeStartTag(DT_tgroup);
+ xmlWriter().writeAttribute("cols",QString::number(tableColumnCount));
+ }
+ writeStartTag(DT_thead);
+ xmlWriter().writeAttribute("valign","top");
+ writeStartTag(DT_row);
+ xmlWriter().writeAttribute("valign","top");
+ inTableHeader = true;
+ inTableBody = false;
+ break;
+ case Atom::TableHeaderRight:
+ writeEndTag(); //
+ if (matchAhead(atom, Atom::TableHeaderLeft)) {
+ skipAhead = 1;
+ writeStartTag(DT_row);
+ xmlWriter().writeAttribute("valign","top");
+ }
+ else {
+ writeEndTag(); //
+ inTableHeader = false;
+ inTableBody = true;
+ writeStartTag(DT_tbody);
+ }
+ break;
+ case Atom::TableRowLeft:
+ if (!inTableHeader && !inTableBody) {
+ inTableBody = true;
+ writeStartTag(DT_tbody);
+ }
+ writeStartTag(DT_row);
+ attr = atom->string();
+ if (!attr.isEmpty()) {
+ if (attr.contains('=')) {
+ int index = 0;
+ int from = 0;
+ QString values;
+ while (index >= 0) {
+ index = attr.indexOf('"',from);
+ if (index >= 0) {
+ ++index;
+ from = index;
+ index = attr.indexOf('"',from);
+ if (index > from) {
+ if (!values.isEmpty())
+ values.append(' ');
+ values += attr.mid(from,index-from);
+ from = index+1;
+ }
+ }
+ }
+ attr = values;
+ }
+ xmlWriter().writeAttribute("outputclass", attr);
+ }
+ xmlWriter().writeAttribute("valign","top");
+ break;
+ case Atom::TableRowRight:
+ writeEndTag(); //
+ break;
+ case Atom::TableItemLeft:
+ {
+ QString values;
+ writeStartTag(DT_entry);
+ for (int i=0; icount(); ++i) {
+ attr = atom->string(i);
+ if (attr.contains('=')) {
+ int index = 0;
+ int from = 0;
+ while (index >= 0) {
+ index = attr.indexOf('"',from);
+ if (index >= 0) {
+ ++index;
+ from = index;
+ index = attr.indexOf('"',from);
+ if (index > from) {
+ if (!values.isEmpty())
+ values.append(' ');
+ values += attr.mid(from,index-from);
+ from = index+1;
+ }
+ }
+ }
+ }
+ else {
+ QStringList spans = attr.split(QLatin1Char(','));
+ if (spans.size() == 2) {
+ if ((spans[0].toInt()>1) || (spans[1].toInt()>1)) {
+ values += "span(" + spans[0] + QLatin1Char(',') + spans[1] + QLatin1Char(')');
+ }
+ }
+ }
+ }
+ if (!values.isEmpty())
+ xmlWriter().writeAttribute("outputclass",values);
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ }
+ break;
+ case Atom::TableItemRight:
+ if (inTableHeader)
+ writeEndTag(); //
+ else {
+ writeEndTag(); //
+ }
+ if (matchAhead(atom, Atom::ParaLeft))
+ skipAhead = 1;
+ break;
+ case Atom::TableOfContents:
+ {
+ int numColumns = 1;
+ const Node* node = relative;
+
+ Doc::Sections sectionUnit = Doc::Section4;
+ QStringList params = atom->string().split(QLatin1Char(','));
+ QString columnText = params.at(0);
+ QStringList pieces = columnText.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ if (pieces.size() >= 2) {
+ columnText = pieces.at(0);
+ pieces.pop_front();
+ QString path = pieces.join(" ").trimmed();
+ node = findNodeForTarget(path, relative, marker, atom);
+ }
+
+ if (params.size() == 2) {
+ numColumns = qMax(columnText.toInt(), numColumns);
+ sectionUnit = (Doc::Sections)params.at(1).toInt();
+ }
+
+ if (node)
+ generateTableOfContents(node,
+ marker,
+ sectionUnit,
+ numColumns,
+ relative);
+ }
+ break;
+ case Atom::Target:
+ if (in_para) {
+ writeEndTag(); //
+ in_para = false;
+ }
+ writeStartTag(DT_p);
+ writeGuidAttribute(Doc::canonicalTitle(atom->string()));
+ xmlWriter().writeAttribute("outputclass","target");
+ //xmlWriter().writeCharacters(protectEnc(atom->string()));
+ writeEndTag(); //
+ break;
+ case Atom::UnhandledFormat:
+ writeStartTag(DT_b);
+ xmlWriter().writeAttribute("outputclass","error");
+ xmlWriter().writeCharacters("");
+ writeEndTag(); //
+ break;
+ case Atom::UnknownCommand:
+ writeStartTag(DT_b);
+ xmlWriter().writeAttribute("outputclass","error unknown-command");
+ writeCharacters(protectEnc(atom->string()));
+ writeEndTag(); //
+ break;
+ case Atom::QmlText:
+ case Atom::EndQmlText:
+ // don't do anything with these. They are just tags.
+ break;
+ default:
+ // unknownAtom(atom);
+ break;
+ }
+ return skipAhead;
+}
+
+/*!
+ Generate a element (and all the stuff inside it)
+ for the C++ class represented by \a innerNode. \a marker is
+ for marking up the code. I don't know what that means exactly.
+ */
+void
+DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker)
+{
+ QList::ConstIterator s;
+
+ QString title;
+ QString rawTitle;
+ QString fullTitle;
+ if (inner->type() == Node::Namespace) {
+ const NamespaceNode* nsn = const_cast(static_cast(inner));
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle + " Namespace";
+
+ /*
+ Note: Because the C++ specialization we are using
+ has no element, we are using the
+ element with an outputclass attribute
+ set to "namespace" .
+ */
+ generateHeader(inner, fullTitle);
+ generateBrief(inner, marker); //
+ writeProlog(inner);
+
+ writeStartTag(DT_cxxClassDetail);
+ writeStartTag(DT_cxxClassDefinition);
+ writeLocation(nsn);
+ writeEndTag(); //
+
+ enterApiDesc(QString(),title);
+ Text brief = nsn->doc().briefText(); // zzz
+ if (!brief.isEmpty()) {
+ writeStartTag(DT_p);
+ generateText(brief, nsn, marker);
+ writeEndTag(); //
+ }
+ generateIncludes(nsn, marker);
+ generateStatus(nsn, marker);
+ generateThreadSafeness(nsn, marker);
+ generateSince(nsn, marker);
+
+ enterSection("h2","Detailed Description");
+ generateBody(nsn, marker);
+ generateAlsoList(nsn, marker);
+ leaveSection();
+ leaveSection(); //
+
+ bool needOtherSection = false;
+ QList summarySections;
+ summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
+ if (!summarySections.isEmpty()) {
+ enterSection("redundant",QString());
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
+ if (!s->inherited.isEmpty())
+ needOtherSection = true;
+ }
+ else {
+ QString attr;
+ if (!s->members.isEmpty()) {
+ writeStartTag(DT_p);
+ attr = cleanRef((*s).name).toLower() + " h2";
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ generateSection(s->members, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ if (!s->reimpMembers.isEmpty()) {
+ QString name = QString("Reimplemented ") + (*s).name;
+ attr = cleanRef(name).toLower() + " h2";
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc(name));
+ writeEndTag(); //
+ generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ }
+ ++s;
+ }
+ if (needOtherSection) {
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass","h3");
+ xmlWriter().writeCharacters("Additional Inherited Members");
+ writeEndTag(); //
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty())
+ generateSectionInheritedList(*s, inner, marker);
+ ++s;
+ }
+ }
+ leaveSection();
+ }
+
+ writeEndTag(); //
+
+ // not included:
+ // not included:
+
+ QList detailSections;
+ detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if ((*s).name == "Classes") {
+ writeNestedClasses((*s),nsn);
+ break;
+ }
+ ++s;
+ }
+
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if ((*s).name == "Function Documentation") {
+ writeFunctions((*s),nsn,marker);
+ }
+ else if ((*s).name == "Type Documentation") {
+ writeEnumerations((*s),marker);
+ writeTypedefs((*s),marker);
+ }
+ else if ((*s).name == "Namespaces") {
+ qDebug() << "Nested namespaces" << outFileName();
+ }
+ else if ((*s).name == "Macro Documentation") {
+ //writeMacros((*s),marker);
+ }
+ ++s;
+ }
+
+ generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
+ generateLowStatusMembers(inner,marker,CodeMarker::Compat);
+ writeEndTag(); //
+ }
+ else if (inner->type() == Node::Class) {
+ const ClassNode* cn = const_cast(static_cast(inner));
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle + " Class";
+
+ generateHeader(inner, fullTitle);
+ generateBrief(inner, marker); //
+ writeProlog(inner);
+
+ writeStartTag(DT_cxxClassDetail);
+ writeStartTag(DT_cxxClassDefinition);
+ writeStartTag(DT_cxxClassAccessSpecifier);
+ xmlWriter().writeAttribute("value",inner->accessString());
+ writeEndTag(); //
+ if (cn->isAbstract()) {
+ writeStartTag(DT_cxxClassAbstract);
+ xmlWriter().writeAttribute("name","abstract");
+ xmlWriter().writeAttribute("value","abstract");
+ writeEndTag(); //
+ }
+ writeDerivations(cn, marker); //
+
+ // not included:
+
+ writeLocation(cn);
+ writeEndTag(); //
+
+ enterApiDesc(QString(),title);
+ Text brief = cn->doc().briefText(); // zzz
+ if (!brief.isEmpty()) {
+ writeStartTag(DT_p);
+ generateText(brief, cn, marker);
+ writeEndTag(); //
+ }
+ generateIncludes(cn, marker);
+ generateStatus(cn, marker);
+ generateInherits(cn, marker);
+ generateInheritedBy(cn, marker);
+ generateThreadSafeness(cn, marker);
+ generateSince(cn, marker);
+ enterSection("h2","Detailed Description");
+ generateBody(cn, marker);
+ generateAlsoList(cn, marker);
+ leaveSection();
+ leaveSection(); //
+
+ bool needOtherSection = false;
+ QList summarySections;
+ summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
+ if (!summarySections.isEmpty()) {
+ enterSection("redundant",QString());
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
+ if (!s->inherited.isEmpty())
+ needOtherSection = true;
+ }
+ else {
+ QString attr;
+ if (!s->members.isEmpty()) {
+ writeStartTag(DT_p);
+ attr = cleanRef((*s).name).toLower() + " h2";
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ generateSection(s->members, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ if (!s->reimpMembers.isEmpty()) {
+ QString name = QString("Reimplemented ") + (*s).name;
+ attr = cleanRef(name).toLower() + " h2";
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc(name));
+ writeEndTag(); //
+ generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ }
+ ++s;
+ }
+ if (needOtherSection) {
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass","h3");
+ xmlWriter().writeCharacters("Additional Inherited Members");
+ writeEndTag(); //
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty())
+ generateSectionInheritedList(*s, inner, marker);
+ ++s;
+ }
+ }
+ leaveSection();
+ }
+
+ // not included: or
+
+ writeEndTag(); //
+
+ // not included:
+ // not included:
+
+ QList detailSections;
+ detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if ((*s).name == "Member Function Documentation") {
+ writeFunctions((*s),cn,marker);
+ }
+ else if ((*s).name == "Member Type Documentation") {
+ writeEnumerations((*s),marker);
+ writeTypedefs((*s),marker);
+ }
+ else if ((*s).name == "Member Variable Documentation") {
+ writeDataMembers((*s),marker);
+ }
+ else if ((*s).name == "Property Documentation") {
+ writeProperties((*s),marker);
+ }
+ else if ((*s).name == "Macro Documentation") {
+ //writeMacros((*s),marker);
+ }
+ else if ((*s).name == "Related Non-Members") {
+ QString attribute("related-non-member");
+ writeFunctions((*s),cn,marker,attribute);
+ }
+ ++s;
+ }
+
+ generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
+ generateLowStatusMembers(inner,marker,CodeMarker::Compat);
+ writeEndTag(); //
+ }
+ else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) {
+ const FakeNode* fn = const_cast(static_cast(inner));
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle;
+
+ /*
+ Note: Because the C++ specialization we are using
+ has no element, we are using the
+ element with an outputclass attribute
+ set to "headerfile" .
+ */
+ generateHeader(inner, fullTitle);
+ generateBrief(inner, marker); //
+ writeProlog(inner);
+
+ writeStartTag(DT_cxxClassDetail);
+ enterApiDesc(QString(),title);
+ Text brief = fn->doc().briefText(); // zzz
+ if (!brief.isEmpty()) {
+ writeStartTag(DT_p);
+ generateText(brief, fn, marker);
+ writeEndTag(); //
+ }
+ generateIncludes(fn, marker);
+ generateStatus(fn, marker);
+ generateThreadSafeness(fn, marker);
+ generateSince(fn, marker);
+ generateSince(fn, marker);
+ enterSection("h2","Detailed Description");
+ generateBody(fn, marker);
+ generateAlsoList(fn, marker);
+ leaveSection();
+ leaveSection(); //
+
+ bool needOtherSection = false;
+ QList summarySections;
+ summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay);
+ if (!summarySections.isEmpty()) {
+ enterSection("redundant",QString());
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty() && s->reimpMembers.isEmpty()) {
+ if (!s->inherited.isEmpty())
+ needOtherSection = true;
+ }
+ else {
+ QString attr;
+ if (!s->members.isEmpty()) {
+ writeStartTag(DT_p);
+ attr = cleanRef((*s).name).toLower() + " h2";
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ generateSection(s->members, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ if (!s->reimpMembers.isEmpty()) {
+ QString name = QString("Reimplemented ") + (*s).name;
+ attr = cleanRef(name).toLower() + " h2";
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc(name));
+ writeEndTag(); //
+ generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
+ generateSectionInheritedList(*s, inner, marker);
+ }
+ }
+ ++s;
+ }
+ if (needOtherSection) {
+ enterSection("additional-inherited-members redundant",QString());
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass","h3");
+ xmlWriter().writeCharacters("Additional Inherited Members");
+ writeEndTag(); //
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ if (s->members.isEmpty())
+ generateSectionInheritedList(*s, inner, marker);
+ ++s;
+ }
+ }
+ leaveSection();
+ }
+
+ writeEndTag(); //
+
+ // not included:
+ // not included:
+
+ QList detailSections;
+ detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if ((*s).name == "Classes") {
+ writeNestedClasses((*s),fn);
+ break;
+ }
+ ++s;
+ }
+
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if ((*s).name == "Function Documentation") {
+ writeFunctions((*s),fn,marker);
+ }
+ else if ((*s).name == "Type Documentation") {
+ writeEnumerations((*s),marker);
+ writeTypedefs((*s),marker);
+ }
+ else if ((*s).name == "Namespaces") {
+ qDebug() << "Nested namespaces" << outFileName();
+ }
+ else if ((*s).name == "Macro Documentation") {
+ //writeMacros((*s),marker);
+ }
+ ++s;
+ }
+ generateLowStatusMembers(inner,marker,CodeMarker::Obsolete);
+ generateLowStatusMembers(inner,marker,CodeMarker::Compat);
+ writeEndTag(); //
+ }
+ else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) {
+ const QmlClassNode* qcn = const_cast(static_cast(inner));
+ const ClassNode* cn = qcn->classNode();
+ rawTitle = marker->plainName(inner);
+ fullTitle = marker->plainFullName(inner);
+ title = rawTitle + " Element";
+ //QString fullTitle = fake->fullTitle();
+ //QString htmlTitle = fullTitle;
+
+ generateHeader(inner, fullTitle);
+ generateBrief(inner, marker); //
+ writeProlog(inner);
+
+ writeStartTag(DT_cxxClassDetail);
+ enterApiDesc(QString(),title);
+ Text brief = qcn->doc().briefText(); // zzz
+ if (!brief.isEmpty()) {
+ writeStartTag(DT_p);
+ generateText(brief, qcn, marker);
+ writeEndTag(); //
+ }
+ generateQmlInstantiates(qcn, marker);
+ generateQmlInherits(qcn, marker);
+ generateQmlInheritedBy(qcn, marker);
+ generateSince(qcn, marker);
+ enterSection("h2","Detailed Description");
+ generateBody(qcn, marker);
+ if (cn) {
+ generateQmlText(cn->doc().body(), cn, marker, qcn->name());
+ generateAlsoList(cn, marker);
+ }
+ leaveSection();
+ leaveSection(); //
+
+ QList summarySections;
+ summarySections = marker->qmlSections(qcn,CodeMarker::Summary);
+ if (!summarySections.isEmpty()) {
+ enterSection("redundant",QString());
+ s = summarySections.begin();
+ while (s != summarySections.end()) {
+ QString attr;
+ if (!s->members.isEmpty()) {
+ writeStartTag(DT_p);
+ attr = cleanRef((*s).name).toLower() + " h2";
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ generateQmlSummary(*s,qcn,marker);
+ //generateSection(s->members, inner, marker, CodeMarker::Summary);
+ //generateSectionInheritedList(*s, inner, marker);
+ }
+ ++s;
+ }
+ leaveSection();
+ }
+
+ QList detailSections;
+ detailSections = marker->qmlSections(qcn,CodeMarker::Detailed);
+ if (!detailSections.isEmpty()) {
+ enterSection("details",QString());
+ s = detailSections.begin();
+ while (s != detailSections.end()) {
+ if (!s->members.isEmpty()) {
+ QString attr;
+ writeStartTag(DT_p);
+ attr = cleanRef((*s).name).toLower() + " h2";
+ xmlWriter().writeAttribute("outputclass",attr);
+ writeCharacters(protectEnc((*s).name));
+ writeEndTag(); //
+ NodeList::ConstIterator m = (*s).members.begin();
+ while (m != (*s).members.end()) {
+ generateDetailedQmlMember(*m, qcn, marker);
+ ++m;
+ }
+ }
+ ++s;
+ }
+ leaveSection();
+ }
+ writeEndTag(); //
+ writeEndTag(); //
+ }
+}
+
+
+/*!
+ Write a list item for a \a link with the given \a text.
+ */
+void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text)
+{
+ writeStartTag(DT_li);
+ writeStartTag(DT_xref);
+ // formathtml
+ writeHrefAttribute(link);
+ writeCharacters(text);
+ writeEndTag(); //
+ writeEndTag(); //
+}
+
+/*!
+ Generate the DITA page for a qdoc file that doesn't map
+ to an underlying c++ file.
+ */
+void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker)
+{
+ /*
+ If the fake node is a page node, and if the page type
+ is DITA map page, write the node's contents as a dita
+ map and return without doing anything else.
+ */
+ if (fake->subType() == Node::Page && fake->pageType() == Node::DitaMapPage) {
+ const DitaMapNode* dmn = static_cast(fake);
+ writeDitaMap(dmn);
+ return;
+ }
+
+ QList sections;
+ QList::const_iterator s;
+ QString fullTitle = fake->fullTitle();
+
+ if (fake->subType() == Node::QmlBasicType) {
+ fullTitle = "QML Basic Type: " + fullTitle;
+ }
+ else if (fake->subType() == Node::Collision) {
+ fullTitle = "Name Collision: " + fullTitle;
+ }
+
+ generateHeader(fake, fullTitle);
+ generateBrief(fake, marker); //
+ writeProlog(fake);
+
+ writeStartTag(DT_body);
+ enterSection(QString(),QString());
+ if (fake->subType() == Node::Module) {
+ generateStatus(fake, marker);
+ if (moduleNamespaceMap.contains(fake->name())) {
+ enterSection("h2","Namespaces");
+ generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
+ leaveSection();
+ }
+ if (moduleClassMap.contains(fake->name())) {
+ enterSection("h2","Classes");
+ generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
+ leaveSection();
+ }
+ }
+
+ if (fake->doc().isEmpty()) {
+ if (fake->subType() == Node::File) {
+ Text text;
+ Quoter quoter;
+ writeStartTag(DT_p);
+ xmlWriter().writeAttribute("outputclass", "small-subtitle");
+ text << fake->subTitle();
+ generateText(text, fake, marker);
+ writeEndTag(); //
+ Doc::quoteFromFile(fake->doc().location(), quoter, fake->name());
+ QString code = quoter.quoteTo(fake->location(), "", "");
+ text.clear();
+ text << Atom(Atom::Code, code);
+ generateText(text, fake, marker);
+ }
+ }
+ else {
+ if (fake->subType() == Node::Module) {
+ enterSection("h2","Detailed Description");
+ generateBody(fake, marker);
+ leaveSection();
+ }
+ else {
+ generateBody(fake, marker);
+ }
+ generateAlsoList(fake, marker);
+
+ if ((fake->subType() == Node::QmlModule) && !fake->qmlModuleMembers().isEmpty()) {
+ NodeMap qmlModuleMembersMap;
+ foreach (const Node* node, fake->qmlModuleMembers()) {
+ if (node->type() == Node::Fake && node->subType() == Node::QmlClass)
+ qmlModuleMembersMap[node->name()] = node;
+ }
+ generateAnnotatedList(fake, marker, qmlModuleMembersMap);
+ }
+ else if (!fake->groupMembers().isEmpty()) {
+ NodeMap groupMembersMap;
+ foreach (const Node *node, fake->groupMembers()) {
+ if (node->type() == Node::Class || node->type() == Node::Namespace)
+ groupMembersMap[node->name()] = node;
+ }
+ generateAnnotatedList(fake, marker, groupMembersMap);
+ }
+ }
+ leaveSection(); //
+ writeEndTag(); //